function mxGraph(container, model, renderHint)
{
	this.renderHint = renderHint;

	if (mxClient.IS_SVG)
	{
		this.dialect = mxConstants.DIALECT_SVG;
	}
	else if (renderHint == mxConstants.RENDERING_HINT_EXACT && mxClient.IS_IE)
	{
		this.dialect = mxConstants.DIALECT_VML;
	}
	else if (renderHint == mxConstants.RENDERING_HINT_FASTEST)
	{
		this.dialect = mxConstants.DIALECT_STRICTHTML;
	}
	else if (renderHint == mxConstants.RENDERING_HINT_FASTER)
	{
		this.dialect = mxConstants.DIALECT_PREFERHTML;
	}
	else
	{
		this.dialect = mxConstants.DIALECT_MIXEDHTML;
	}
	this.model = (model != null) ? model : new mxGraphModel();
	this.multiplicities = [];
	this.cellRenderer = this.createCellRenderer();
	this.setSelectionModel(this.createSelectionModel());
	this.setStylesheet(this.createStylesheet());
	this.view = this.createGraphView();
	var self = this;
	this.model.addListener(mxEvent.CHANGE, function(sender, evt)
	{
		self.graphModelChanged(evt.getArgAt(0));
	});
	this.tooltipHandler = new mxTooltipHandler(this);
	this.tooltipHandler.setEnabled(false);
	this.panningHandler = new mxPanningHandler(this);
	this.panningHandler.panningEnabled = false;
	this.connectionHandler = new mxConnectionHandler(this);
	this.connectionHandler.setEnabled(false);
	this.graphHandler = new mxGraphHandler(this);

	if (container != null)
	{
		this.init(container);
	}
	this.view.revalidate();
};
mxResources.add(this.scriptBase + 'resources/graph');
mxGraph.prototype = new mxEventSource();
mxGraph.prototype.constructor = mxGraph;
mxGraph.prototype.EMPTY_ARRAY = [];

mxGraph.prototype.mouseListeners = null;
mxGraph.prototype.isMouseDown = false;
mxGraph.prototype.model = null;
mxGraph.prototype.view = null;
mxGraph.prototype.stylesheet = null;
mxGraph.prototype.selectionModel = null;
mxGraph.prototype.cellEditor = null;
mxGraph.prototype.cellRenderer = null;
mxGraph.prototype.multiplicities = null;
mxGraph.prototype.renderHint = null;
mxGraph.prototype.dialect = null;
mxGraph.prototype.gridSize = 10;
mxGraph.prototype.gridEnabled = true;
mxGraph.prototype.tolerance = 4;
mxGraph.prototype.defaultOverlap = 0.5;
mxGraph.prototype.defaultParent = null;
mxGraph.prototype.alternateEdgeStyle = null;
mxGraph.prototype.backgroundImage = null;
mxGraph.prototype.pageVisible = false;
mxGraph.prototype.pageFormat = mxConstants.PAGE_FORMAT_A4_PORTRAIT;
mxGraph.prototype.pageScale = 1.5;
mxGraph.prototype.enabled = true;
mxGraph.prototype.escapeEnabled = true;
mxGraph.prototype.invokesStopCellEditing = true;
mxGraph.prototype.enterStopsCellEditing = false;
mxGraph.prototype.exportEnabled = true;
mxGraph.prototype.importEnabled = true;
mxGraph.prototype.cellsLocked = false;
mxGraph.prototype.cellsCloneable = true;
mxGraph.prototype.foldingEnabled = true;
mxGraph.prototype.cellsEditable = true;
mxGraph.prototype.cellsDeletable = true;
mxGraph.prototype.cellsMovable = true;
mxGraph.prototype.edgeLabelsMovable = true;
mxGraph.prototype.vertexLabelsMovable = false;
mxGraph.prototype.dropEnabled = false;
mxGraph.prototype.splitEnabled = true;
mxGraph.prototype.cellsResizable = true;
mxGraph.prototype.cellsBendable = true;
mxGraph.prototype.cellsSelectable = true;
mxGraph.prototype.cellsDisconnectable = true;
mxGraph.prototype.autoSizeCells = false;
mxGraph.prototype.autoScroll = true;
mxGraph.prototype.autoExtend = true;
mxGraph.prototype.maximumGraphBounds = null;
mxGraph.prototype.minimumGraphSize = null;
mxGraph.prototype.minimumContainerSize = null;
mxGraph.prototype.maximumContainerSize = null;
mxGraph.prototype.resizeContainer = false;
mxGraph.prototype.border = 0;
mxGraph.prototype.ordered = true;
mxGraph.prototype.keepEdgesInForeground = false;
mxGraph.prototype.keepEdgesInBackground = true;
mxGraph.prototype.constrainChildren = true;
mxGraph.prototype.extendParents = true;
mxGraph.prototype.extendParentsOnAdd = true;
mxGraph.prototype.collapseToPreferredSize = true;
mxGraph.prototype.zoomFactor = 1.2;
mxGraph.prototype.keepSelectionVisibleOnZoom = false;
mxGraph.prototype.centerZoom = true;
mxGraph.prototype.resetViewOnRootChange = true;
mxGraph.prototype.resetEdgesOnResize = false;
mxGraph.prototype.resetEdgesOnMove = false;
mxGraph.prototype.resetEdgesOnConnect = true;
mxGraph.prototype.allowLoops = false;
mxGraph.prototype.defaultLoopStyle = mxEdgeStyle.Loop;
mxGraph.prototype.multigraph = true;
mxGraph.prototype.connectableEdges = false;
mxGraph.prototype.allowDanglingEdges = true;
mxGraph.prototype.cloneInvalidEdges = false;
mxGraph.prototype.disconnectOnMove = true;
mxGraph.prototype.labelsVisible = true;
mxGraph.prototype.htmlLabels = false;
mxGraph.prototype.swimlaneSelectionEnabled = true;
mxGraph.prototype.swimlaneNesting = true;
mxGraph.prototype.swimlaneIndicatorColorAttribute = mxConstants.STYLE_FILLCOLOR;
mxGraph.prototype.collapsedImage = new mxImage(mxClient.imageBasePath + 'collapsed.gif', 9, 9);
mxGraph.prototype.expandedImage = new mxImage(mxClient.imageBasePath + 'expanded.gif', 9, 9);
mxGraph.prototype.warningImage = new mxImage(mxClient.imageBasePath + 'warning' + ((mxClient.IS_MAC) ? '.png' : '.gif'),
	16, 16);
mxGraph.prototype.alreadyConnectedResource = (mxClient.language != 'none') ? 'alreadyConnected' : '';
mxGraph.prototype.containsValidationErrorsResource = (mxClient.language != 'none') ? 'containsValidationErrors' : '';
mxGraph.prototype.init = function(container)
{
	this.container = container;
	this.cellEditor = this.createCellEditor();
	this.view.init();
	this.tooltipHandler.init();
	this.panningHandler.init();
	this.connectionHandler.init();
	this.sizeDidChange();

	if (mxClient.IS_IE)
	{
		var self = this;
		mxEvent.addListener(window, 'unload', function()
		{
			self.destroy();
		});
		mxEvent.addListener(container, 'selectstart', function()
		{
			return self.isEditing();
		});
	}
	else
	{

		var self = this;
		this.focusHandler = function(evt)
		{
			self.activeElement = mxEvent.getSource(evt);
		};
		this.blurHandler = function(evt)
		{
			self.activeElement = null;
		}
		mxEvent.addListener(document.body, 'focus', this.focusHandler);
		mxEvent.addListener(document.body, 'blur', this.blurHandler);
	}
};
mxGraph.prototype.createSelectionModel = function()
{
	return new mxGraphSelectionModel(this);
};
mxGraph.prototype.createStylesheet = function()
{
	return new mxStylesheet();
};
mxGraph.prototype.createGraphView = function()
{
	return new mxGraphView(this);
};
mxGraph.prototype.createCellRenderer = function()
{
	return new mxCellRenderer();
};
mxGraph.prototype.createCellEditor = function()
{
	return new mxCellEditor(this);
};
mxGraph.prototype.getModel = function()
{
	return this.model;
};
mxGraph.prototype.getView = function()
{
	return this.view;
};
mxGraph.prototype.getStylesheet = function()
{
	return this.stylesheet;
};
mxGraph.prototype.setStylesheet = function(stylesheet)
{
	this.stylesheet = stylesheet;
};
mxGraph.prototype.getSelectionModel = function()
{
	return this.selectionModel;
};
mxGraph.prototype.setSelectionModel = function(selectionModel)
{
	this.selectionModel = selectionModel;
};
mxGraph.prototype.getSelectionCellsForChanges = function(changes)
{
	var cells = [];

	for (var i = 0; i < changes.length; i++)
	{
		var change = changes[i];

		if (change.constructor != mxRootChange)
		{
			var cell = null;

			if (change.constructor == mxChildChange && change.isAdded)
			{
				cell = change.child;
			}
			else if (change.cell != null && change.cell.constructor == mxCell)
			{
				cell = change.cell;
			}

			if (cell != null && mxUtils.indexOf(cells, cell) < 0)
			{
				cells.push(cell);
			}
		}
	}
	return this.getModel().getTopmostCells(cells);
};
mxGraph.prototype.graphModelChanged = function(changes)
{
	for (var i = 0; i < changes.length; i++)
	{
		this.processChange(changes[i]);
	}
	this.removeSelectionCells(this.getRemovedCellsForChanges(changes));
	this.view.validate();
	this.sizeDidChange();
};
mxGraph.prototype.getRemovedCellsForChanges = function(changes)
{
	var result = [];

	for (var i = 0; i < changes.length; i++)
	{
		var change = changes[i];

		if (change.constructor == mxRootChange)
		{
			break;
		}
		else if (change.constructor == mxChildChange)
		{
			if (!change.isAdded)
			{
				result = result.concat(this.model.getDescendants(change.child));
			}
		}
		else if (change.constructor == mxVisibleChange)
		{
			result = result.concat(this.model.getDescendants(change.cell));
		}
	}
	return result;
};
mxGraph.prototype.processChange = function(change)
{
	if (change.constructor == mxRootChange)
	{
		this.clearSelection();
		this.removeStateForCell(change.previous);

		if (this.resetViewOnRootChange)
		{
			this.view.scale = 1;
			this.view.translate.x = 0;
			this.view.translate.y = 0;
		}
		this.fireEvent(mxEvent.ROOT);
	}
	else if (change.constructor == mxChildChange)
	{
		var newParent = this.model.getParent(change.child);

		if(newParent != null)
		{
			this.view.invalidate(change.child, true, false, true);
		}
		else
		{
			this.removeStateForCell(change.child);
		}

		if (newParent != change.previous)
		{
			if (newParent != null)
			{
				this.view.invalidate(newParent, false, false);
			}

			if (change.previous != null)
			{
				this.view.invalidate(change.previous, false, false);
			}
		}
	}
	else if (change.constructor == mxTerminalChange || change.constructor == mxGeometryChange)
	{
		this.view.invalidate(change.cell);
	}
	else if (change.constructor == mxValueChange)
	{
		this.view.invalidate(change.cell, false, false);
	}
	else if (change.constructor == mxStyleChange)
	{
		this.view.removeState(change.cell);
	}
	else if (change.cell != null && change.cell.constructor == mxCell)
	{
		this.removeStateForCell(change.cell);
	}
};
mxGraph.prototype.removeStateForCell = function(cell)
{
	var childCount = this.model.getChildCount(cell);

	for (var i = 0; i < childCount; i++)
	{
		this.removeStateForCell(this.model.getChildAt(cell, i));
	}
	this.view.removeState(cell);
};

mxGraph.prototype.addCellOverlay = function(cell, overlay)
{
	if (cell.overlays == null)
	{
		cell.overlays = [];
	}
	cell.overlays.push(overlay);
	var state = this.view.getState(cell);

	if (state != null)
	{
		this.cellRenderer.redraw(state);
	}
	this.fireEvent(mxEvent.ADD_OVERLAY, new mxEventObject(
	[
		cell,
		overlay
	]));

	return overlay;
};
mxGraph.prototype.getCellOverlays = function(cell)
{
	return cell.overlays;
};
mxGraph.prototype.removeCellOverlay = function(cell, overlay)
{
	if (overlay == null)
	{
		this.removeCellOverlays(cell);
	}
	else
	{
		var index = mxUtils.indexOf(cell.overlays, overlay);

		if (index >= 0)
		{
			cell.overlays.splice(index, 1);

			if (cell.overlays.length == 0)
			{
				cell.overlays = null;
			}
			var state = this.view.getState(cell);

			if (state != null)
			{
				this.cellRenderer.redraw(state);
			}
			this.fireEvent(mxEvent.REMOVE_OVERLAY, new mxEventObject(
			[
				cell,
				overlay
			]));
		}
		else
		{
			overlay = null;
		}
	}
	return overlay;
};
mxGraph.prototype.removeCellOverlays = function(cell)
{
	var overlays = cell.overlays;

	if (overlays != null)
	{
		cell.overlays = null;
		var state = this.view.getState(cell);

		if (state != null)
		{
			this.cellRenderer.redraw(state);
		}

		for (var i = 0; i < overlays.length; i++)
		{
			this.fireEvent(mxEvent.REMOVE_OVERLAY, new mxEventObject(
			[
				cell,
				overlays[i]
			]));
		}
	}

	return overlays;
};
mxGraph.prototype.clearCellOverlays = function(cell)
{
	cell = (cell != null) ? cell : this.model.getRoot();
	this.removeCellOverlays(cell);
	var childCount = this.model.getChildCount(cell);

	for (var i = 0; i < childCount; i++)
	{
		var child = this.model.getChildAt(cell, i);
		this.clearCellOverlays(child);
	}
};
mxGraph.prototype.setCellWarning = function(cell, warning, img, isSelect)
{
	if (warning != null && warning.length > 0)
	{
		img = (img != null) ? img : this.warningImage;
		var overlay = new mxCellOverlay(img, '<font color=red>' + warning + '</font>');

		if (isSelect)
		{
			var self = this;
			overlay.addListener(mxEvent.CLICK, function(sender, evt)
			{
				if (self.isEnabled())
				{
					self.setSelectionCell(cell);
				}
			});
		}
		return this.addCellOverlay(cell, overlay);
	}
	else
	{
		this.removeCellOverlays(cell);
	}
	return null;
};

mxGraph.prototype.startEditing = function()
{
	this.startEditingAtCell();
};
mxGraph.prototype.startEditingAtCell = function(cell, trigger)
{
	if (cell == null)
	{
		cell = this.getSelectionCell();

		if (cell != null && !this.isCellEditable(cell))
		{
			cell = null;
		}
	}

	if (cell != null)
	{
		this.fireEvent(mxEvent.START_EDITING, new mxEventObject(
		[
			cell,
			trigger
		]));

		this.cellEditor.startEditing(cell, trigger);
	}
};
mxGraph.prototype.getEditingValue = function(cell, trigger)
{
	return this.convertValueToString(cell);
};
mxGraph.prototype.stopEditing = function(cancel)
{
	this.cellEditor.stopEditing(cancel);
};
mxGraph.prototype.labelChanged = function(cell, newValue, trigger)
{
	this.model.beginUpdate();

	try
	{
		this.cellLabelChanged(cell, newValue, this.isAutoSizeCell(cell));
		this.fireEvent(mxEvent.LABEL_CHANGED, new mxEventObject(
		[
			cell,
			newValue,
			trigger
		]));
	}
	finally
	{
		this.model.endUpdate();
	}
	return cell;
};
mxGraph.prototype.cellLabelChanged = function(cell, newValue, autoSize)
{
	this.model.beginUpdate();

	try
	{
		this.model.setValue(cell, newValue);

		if (autoSize)
		{
			this.cellSizeUpdated(cell, false);
		}
	}
	finally
	{
		this.model.endUpdate();
	}
};

mxGraph.prototype.escape = function(evt)
{
	this.stopEditing(true);
	this.connectionHandler.reset();
	this.graphHandler.reset();
	var cells = this.getSelectionCells();

	for (var i = 0; i < cells.length; i++)
	{
		var state = this.view.getState(cells[i]);

		if (state != null && state.handler != null)
		{
			state.handler.reset();
		}
	}
};
mxGraph.prototype.click = function(evt, cell)
{
	this.fireEvent(mxEvent.CLICK, new mxEventObject(
	[
		evt,
		cell
	]));

	if (this.isEnabled() && !mxEvent.isConsumed(evt))
	{
		if (cell != null)
		{
			this.selectCellForEvent(cell, evt);
		}
		else
		{
			var swimlane = null;

			if (this.swimlaneSelectionEnabled)
			{
				var pt = mxUtils.convertPoint(this.container, evt.clientX, evt.clientY);

				swimlane = this.getSwimlaneAt(pt.x, pt.y);
			}

			if (swimlane != null)
			{
				this.selectCellForEvent(swimlane, evt);
			}
			else if (!this.isToggleEvent(evt))
			{
				this.clearSelection();
			}
		}
	}
};
mxGraph.prototype.dblClick = function(evt, cell)
{
	this.fireEvent(mxEvent.DOUBLE_CLICK, new mxEventObject(
	[
		evt,
		cell
	]));

	if (this.isEnabled() && !mxEvent.isConsumed(evt) && cell != null && this.isCellEditable(cell))
	{
		this.startEditingAtCell(cell, evt);
	}
};
mxGraph.prototype.scrollPointToVisible = function(x, y, extend, border)
{
	if (mxUtils.hasScrollbars(this.container))
	{
		var c = this.container;
		border = (border != null) ? border : 20;

		if (x >= c.scrollLeft && y >= c.scrollTop && x <= c.scrollLeft + c.clientWidth && y <= c.scrollTop
			+ c.clientHeight)
		{
			var dx = c.scrollLeft + c.clientWidth - x;

			if (dx < border)
			{
				var old = c.scrollLeft;
				c.scrollLeft += border - dx;

				if (extend && old == c.scrollLeft)
				{
					if (this.dialect == mxConstants.DIALECT_SVG)
					{
						var root = this.view.getDrawPane().ownerSVGElement;
						var width = parseInt(root.getAttribute('width')) + border - dx;

						root.setAttribute('width', width);
					}
					else
					{
						var width = Math.max(c.clientWidth, c.scrollWidth) + border - dx;
						var canvas = this.view.getCanvas();
						canvas.style.width = width + 'px';
					}
					c.scrollLeft += border - dx;
				}
			}
			else
			{
				dx = x - c.scrollLeft;

				if (dx < border)
				{
					c.scrollLeft -= border - dx;
				}
			}
			var dy = c.scrollTop + c.clientHeight - y;

			if (dy < border)
			{
				var old = c.scrollTop;
				c.scrollTop += border - dy;

				if (old == c.scrollTop && extend)
				{
					if (this.dialect == mxConstants.DIALECT_SVG)
					{
						var root = this.view.getDrawPane().ownerSVGElement;
						var height = parseInt(root.getAttribute('height')) + border - dy;

						root.setAttribute('height', height);
					}
					else
					{
						var height = Math.max(c.clientHeight, c.scrollHeight) + border - dy;
						var canvas = this.view.getCanvas();
						canvas.style.height = height + 'px';
					}
					c.scrollTop += border - dy;
				}
			}
			else
			{
				dy = y - c.scrollTop;

				if (dy < border)
				{
					c.scrollTop -= border - dy;
				}
			}
		}
	}
}
mxGraph.prototype.sizeDidChange = function()
{
	var bounds = this.getGraphBounds();

	if (this.container != null)
	{
		var border = this.getBorder();
		var width = bounds.x + bounds.width + 1 + border;
		var height = bounds.y + bounds.height + 1 + border;

		if (this.minimumContainerSize != null)
		{
			width = Math.max(width, this.minimumContainerSize.width);
			height = Math.max(height, this.minimumContainerSize.height);
		}

		if (this.resizeContainer)
		{
			var w = width;
			var h = height;

			if (this.maximumContainerSize != null)
			{
				w = Math.min(this.maximumContainerSize.width, w);
				h = Math.min(this.maximumContainerSize.height, h);
			}
			this.container.style.width = w + 'px';
			this.container.style.height = h + 'px';
		}
		width = Math.max(width, this.container.offsetWidth);
		height = Math.max(height, this.container.offsetHeight);

		if (this.dialect == mxConstants.DIALECT_SVG)
		{
			var root = this.view.getDrawPane().ownerSVGElement;

			if (this.minimumGraphSize != null)
			{
				width = Math.max(width, this.minimumGraphSize.width * this.view.scale);
				height = Math.max(height, this.minimumGraphSize.height * this.view.scale);
			}

			root.setAttribute('width', width);
			root.setAttribute('height', height);
		}
		else
		{
			var drawPane = this.view.getDrawPane();
			var canvas = this.view.getCanvas();
			drawPane.style.width = width + 'px';
			drawPane.style.height = height + 'px';
			canvas.style.width = width + 'px';
			canvas.style.height = height + 'px';

			if (this.minimumGraphSize != null)
			{
				width = Math.max(width, this.minimumGraphSize.width * this.view.scale);
				height = Math.max(height, this.minimumGraphSize.height * this.view.scale);
				canvas.style.width = width + 'px';
				canvas.style.height = height + 'px';
			}
		}
	}
	this.fireEvent(mxEvent.SIZE, new mxEventObject([bounds]));
};

mxGraph.prototype.getCellStyle = function(cell)
{
	var stylename = this.model.getStyle(cell);
	var style = null;

	if (this.model.isEdge(cell))
	{
		style = this.stylesheet.getDefaultEdgeStyle();
	}
	else
	{
		style = this.stylesheet.getDefaultVertexStyle();
	}

	if (stylename != null)
	{
		style = this.stylesheet.getCellStyle(stylename, style);
	}

	if (style == null)
	{
		style = mxGraph.prototype.EMPTY_ARRAY;
	}
	return style;
};
mxGraph.prototype.setCellStyle = function(style, cells)
{
	cells = cells || this.getSelectionCells();

	if (cells != null)
	{
		this.model.beginUpdate();

		try
		{
			for (var i = 0; i < cells.length; i++)
			{
				this.model.setStyle(cells[i], style);
			}
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};
mxGraph.prototype.toggleCellStyle = function(key, defaultValue, cell)
{
	cell = cell || this.getSelectionCell();
	this.toggleCellStyles(key, defaultValue, [cell]);
};
mxGraph.prototype.toggleCellStyles = function(key, defaultValue, cells)
{
	defaultValue = (defaultValue != null) ? defaultValue : false;
	cells = cells || this.getSelectionCells();

	if (cells != null && cells.length > 0)
	{
		var state = this.view.getState(cells[0]);
		var style = (state != null) ? state.style : this.getCellStyle(cells[0]);

		if (style != null)
		{
			var val = (mxUtils.getValue(style, key, defaultValue)) ? 0 : 1;
			this.setCellStyles(key, val, cells);
		}
	}
}
mxGraph.prototype.setCellStyles = function(key, value, cells)
{
	cells = cells || this.getSelectionCells();
	mxUtils.setCellStyles(this.model, cells, key, value);
};
mxGraph.prototype.toggleCellStyleFlags = function(key, flag, cells)
{
	this.setCellStyleFlags(key, flag, null, cells);
};
mxGraph.prototype.setCellStyleFlags = function(key, flag, value, cells)
{
	cells = cells || this.getSelectionCells();

	if (cells != null && cells.length > 0)
	{
		if (value == null)
		{
			var state = this.view.getState(cells[0]);
			var style = (state != null) ? state.style : this.getCellStyle(cells[0]);

			if (style != null)
			{
				var current = parseInt(style[key] || 0);
				value = !((current & flag) == flag);
			}
		}
		mxUtils.setCellStyleFlags(this.model, cells, key, flag, value);
	}
};

mxGraph.prototype.alignCells = function(align, cells, param)
{
	if (cells == null)
	{
		cells = this.getSelectionCells();
	}

	if (cells != null && cells.length > 1)
	{
		if (param == null)
		{
			for (var i = 0; i < cells.length; i++)
			{
				var geo = this.getCellGeometry(cells[i]);

				if (geo != null && !this.model.isEdge(cells[i]))
				{
					if (param == null)
					{
						if (align == mxConstants.ALIGN_CENTER)
						{
							param = geo.x + geo.width / 2;
							break;
						}
						else if (align == mxConstants.ALIGN_RIGHT)
						{
							param = geo.x + geo.width;
						}
						else if (align == mxConstants.ALIGN_TOP)
						{
							param = geo.y;
						}
						else if (align == mxConstants.ALIGN_MIDDLE)
						{
							param = geo.y + geo.height / 2;
							break;
						}
						else if (align == mxConstants.ALIGN_BOTTOM)
						{
							param = geo.y + geo.height;
						}
						else
						{
							param = geo.x;
						}
					}
					else
					{
						if (align == mxConstants.ALIGN_RIGHT)
						{
							param = Math.max(param, geo.x + geo.width);
						}
						else if (align == mxConstants.ALIGN_TOP)
						{
							param = Math.min(param, geo.y);
						}
						else if (align == mxConstants.ALIGN_BOTTOM)
						{
							param = Math.max(param, geo.y + geo.height);
						}
						else
						{
							param = Math.min(param, geo.x);
						}
					}
				}
			}
		}
		this.model.beginUpdate();

		try
		{
			for (var i = 0; i < cells.length; i++)
			{
				var geo = this.getCellGeometry(cells[i]);

				if (geo != null && !this.model.isEdge(cells[i]))
				{
					geo = geo.clone();

					if (align == mxConstants.ALIGN_CENTER)
					{
						geo.x = param - geo.width / 2;
					}
					else if (align == mxConstants.ALIGN_RIGHT)
					{
						geo.x = param - geo.width;
					}
					else if (align == mxConstants.ALIGN_TOP)
					{
						geo.y = param;
					}
					else if (align == mxConstants.ALIGN_MIDDLE)
					{
						geo.y = param - geo.height / 2;
					}
					else if (align == mxConstants.ALIGN_BOTTOM)
					{
						geo.y = param - geo.height;
					}
					else
					{
						geo.x = param;
					}
					this.model.setGeometry(cells[i], geo);
				}
			}
			this.fireEvent(mxEvent.ALIGN_CELLS, new mxEventObject([cells]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
	return cells;
};
mxGraph.prototype.flipEdge = function(edge)
{
	if (edge != null && this.alternateEdgeStyle != null)
	{
		this.model.beginUpdate();

		try
		{
			var style = this.model.getStyle(edge);

			if (style == null || style.length == 0)
			{
				this.model.setStyle(edge, this.alternateEdgeStyle);
			}
			else
			{
				this.model.setStyle(edge, null);
			}
			this.resetEdge(edge);
			this.fireEvent(mxEvent.FLIP_EDGE, new mxEventObject([edge]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
	return edge;
};

mxGraph.prototype.orderCells = function(back, cells)
{
	if (cells == null)
	{
		cells = mxUtils.sortCells(this.getSelectionCells(), true);
	}
	this.model.beginUpdate();

	try
	{
		this.cellsOrdered(cells, back);
		this.fireEvent(mxEvent.ORDER_CELLS, new mxEventObject(
		[
			back,
			cells
		]));
	}
	finally
	{
		this.model.endUpdate();
	}
	return cells;
};
mxGraph.prototype.cellsOrdered = function(cells, back)
{
	if (cells != null)
	{
		this.model.beginUpdate();

		try
		{
			for (var i = 0; i < cells.length; i++)
			{
				var parent = this.model.getParent(cells[i]);

				if (back)
				{
					this.model.add(parent, cells[i], i);
				}
				else
				{
					this.model.add(parent, cells[i], this.model.getChildCount(parent) - 1);
				}
			}
			this.fireEvent(mxEvent.CELLS_ORDERED, new mxEventObject(
			[
				cells,
				back
			]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};

mxGraph.prototype.groupCells = function(group, border, cells)
{
	if (cells == null)
	{
		cells = mxUtils.sortCells(this.getSelectionCells(), true);
	}
	cells = this.getCellsForGroup(cells);
	var bounds = this.getBoundsForGroup(group, cells, border);

	if (cells.length > 1 && bounds != null)
	{
		var parent = this.model.getParent(cells[0]);
		this.model.beginUpdate();

		try
		{
			if (group == null)
			{
				group = this.createGroupCell(cells);
			}

			if (this.getCellGeometry(group) == null)
			{
				this.model.setGeometry(group, new mxGeometry());
			}
			var index = this.model.getChildCount(group);
			this.cellsAdded(cells, group, index, null, null, false);
			this.cellsMoved(cells, -bounds.x, -bounds.y, false, true);
			index = this.model.getChildCount(parent);
			this.cellsAdded([group], parent, index, null, null, false);
			this.cellsResized([group], [bounds]);
			this.fireEvent(mxEvent.GROUP_CELLS, new mxEventObject(
			[
				group,
				border,
				cells
			]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
	return group;
};
mxGraph.prototype.getCellsForGroup = function(cells)
{
	var result = [];

	if (cells != null && cells.length > 1)
	{
		var parent = this.model.getParent(cells[0]);
		result.push(cells[0]);

		for (var i = 1; i < cells.length; i++)
		{
			if (this.model.getParent(cells[i]) == parent)
			{
				result.push(cells[i]);
			}
		}
	}
	return result;
};
mxGraph.prototype.getBoundsForGroup = function(group, children, border)
{
	var bounds = this.view.getBounds(children);

	if (children != null && children.length > 0 && bounds != null)
	{
		var parent = this.model.getParent(children[0]);
		var pstate = this.view.getState(parent);
		var scale = this.view.getScale();
		var tr = this.view.getTranslate();
		var x = bounds.x - pstate.origin.x * scale;
		var y = bounds.y - pstate.origin.y * scale;
		var width = bounds.width;
		var height = bounds.height;

		if (this.isSwimlane(group))
		{
			var size = this.getStartSize(group);
			x -= size.width;
			width += size.width;
			y -= size.height;
			height += size.height;
		}
		bounds = new mxRectangle(x / scale - border - tr.x, y / scale - border - tr.y, width / scale + 2 * border,
			height / scale + 2 * border);
	}
	return bounds;
};
mxGraph.prototype.createGroupCell = function(cells)
{
	var group = new mxCell('');
	group.setVertex(true);
	group.setConnectable(false);
	return group;
};
mxGraph.prototype.ungroupCells = function(cells)
{
	var result = [];

	if (cells == null)
	{
		cells = this.getSelectionCells();
		var tmp = [];

		for (var i = 0; i < cells.length; i++)
		{
			if (this.model.getChildCount(cells[i]) > 0)
			{
				tmp.push(cells[i]);
			}
		}
		cells = tmp;
	}

	if (cells != null && cells.length > 0)
	{
		this.model.beginUpdate();

		try
		{
			for (var i = 0; i < cells.length; i++)
			{
				var children = this.model.getChildren(cells[i]);

				if (children != null && children.length > 0)
				{
					children = children.slice();
					var parent = this.model.getParent(cells[i]);
					var index = this.model.getChildCount(parent);
					this.cellsAdded(children, parent, index, null, null, true);
					result = result.concat(children);
				}
			}
			this.cellsRemoved(this.addAllEdges(cells));
			this.fireEvent(mxEvent.UNGROUP_CELLS, new mxEventObject([cells]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
	return result;
};
mxGraph.prototype.removeCellsFromParent = function(cells)
{
	if (cells == null)
	{
		cells = this.getSelectionCells();
	}
	this.model.beginUpdate();

	try
	{
		var parent = this.getDefaultParent();
		var index = this.model.getChildCount(parent);
		this.cellsAdded(cells, parent, index, null, null, true);
		this.fireEvent(mxEvent.REMOVE_CELLS_FROM_PARENT, new mxEventObject([cells]));
	}
	finally
	{
		this.model.endUpdate();
	}
	return cells;
};

mxGraph.prototype.cloneCells = function(cells, allowInvalidEdges)
{
	allowInvalidEdges = (allowInvalidEdges != null) ? allowInvalidEdges : true;
	var clones = null;

	if (cells != null)
	{
		var hash = {};
		var tmp = [];

		for (var i = 0; i < cells.length; i++)
		{
			var id = mxCellPath.create(cells[i]);
			hash[id] = cells[i];
			tmp.push(cells[i]);
		}

		if (tmp.length > 0)
		{
			var scale = this.view.scale;
			var trans = this.view.translate;
			clones = this.model.cloneCells(cells, true);

			for (var i = 0; i < cells.length; i++)
			{
				if (!allowInvalidEdges && this.model.isEdge(clones[i])
					&& this.getEdgeValidationError(clones[i], this.model.getTerminal(clones[i], true),
						this.model.getTerminal(clones[i], false)) != null)
				{
					clones[i] = null;
				}
				else
				{
					var g = this.model.getGeometry(clones[i]);

					if (g != null)
					{
						var state = this.view.getState(cells[i]);
						var pstate = this.view.getState(this.model.getParent(cells[i]));

						if (state != null && pstate != null)
						{
							var dx = pstate.origin.x;
							var dy = pstate.origin.y;

							if (this.model.isEdge(clones[i]))
							{
								var pts = state.absolutePoints;
								var src = this.model.getTerminal(cells[i], true);
								var srcId = mxCellPath.create(src);

								while (src != null && hash[srcId] == null)
								{
									src = this.model.getParent(src);
									srcId = mxCellPath.create(src);
								}

								if (src == null)
								{
									g.setTerminalPoint(new mxPoint(pts[0].x / scale - trans.x,
										pts[0].y / scale - trans.y), true);
								}
								var trg = this.model.getTerminal(cells[i], false);
								var trgId = mxCellPath.create(trg);

								while (trg != null && hash[trgId] == null)
								{
									trg = this.model.getParent(trg);
									trgId = mxCellPath.create(trg);
								}

								if (trg == null)
								{
									var n = pts.length - 1;
									g.setTerminalPoint(new mxPoint(pts[n].x / scale - trans.x,
										pts[n].y / scale - trans.y), false);
								}
								var points = g.points;

								if (points != null)
								{
									for (var j = 0; j < points.length; j++)
									{
										points[j].x += dx;
										points[j].y += dy;
									}
								}
							}
							else
							{
								g.x += dx;
								g.y += dy;
							}
						}
					}
				}
			}
		}
		else
		{
			clones = [];
		}
	}
	return clones;
};
mxGraph.prototype.insertVertex = function(parent, id, value, x, y, width, height, style)
{
	var vertex = this.createVertex(parent, id, value, x, y, width, height, style);
	return this.addCell(vertex, parent);
};
mxGraph.prototype.createVertex = function(parent, id, value, x, y, width, height, style)
{
	var geometry = new mxGeometry(x, y, width, height);
	var vertex = new mxCell(value, geometry, style);
	vertex.setId(id);
	vertex.setVertex(true);
	vertex.setConnectable(true);
	return vertex;
};
mxGraph.prototype.insertEdge = function(parent, id, value, source, target, style)
{
	var edge = this.createEdge(parent, id, value, source, target, style);
	return this.addEdge(edge, parent, source, target);
};
mxGraph.prototype.createEdge = function(parent, id, value, source, target, style)
{
	var edge = new mxCell(value, new mxGeometry(), style);
	edge.setId(id);
	edge.setEdge(true);
	edge.geometry.relative = true;
	return edge;
};
mxGraph.prototype.addEdge = function(edge, parent, source, target, index)
{
	return this.addCell(edge, parent, index, source, target);
};
mxGraph.prototype.addCell = function(cell, parent, index, source, target)
{
	return this.addCells([cell], parent, index, source, target)[0];
};
mxGraph.prototype.addCells = function(cells, parent, index, source, target)
{
	if (parent == null)
	{
		parent = this.getDefaultParent();
	}

	if (index == null)
	{
		index = this.model.getChildCount(parent);
	}
	this.model.beginUpdate();

	try
	{
		this.cellsAdded(cells, parent, index, source, target, false);
		this.fireEvent(mxEvent.ADD_CELLS, new mxEventObject(
		[
			cells,
			parent,
			index,
			source,
			target
		]));
	}
	finally
	{
		this.model.endUpdate();
	}
	return cells;
};
mxGraph.prototype.cellsAdded = function(cells, parent, index, source, target, absolute)
{
	if (cells != null && parent != null && index != null)
	{
		this.model.beginUpdate();

		try
		{
			var parentState = (absolute) ? this.view.getState(parent) : null;
			var o1 = (parentState != null) ? parentState.origin : null;
			var zero = new mxPoint(0, 0);

			for (var i = 0; i < cells.length; i++)
			{
				var previous = this.model.getParent(cells[i]);

				if (o1 != null && cells[i] != parent && parent != previous)
				{
					var oldState = this.view.getState(previous);
					var o2 = (oldState != null) ? oldState.origin : zero;
					var geo = this.model.getGeometry(cells[i]);

					if (geo != null)
					{
						var dx = o2.x - o1.x;
						var dy = o2.y - o1.y;
						this.model.setGeometry(cells[i], geo.translate(dx, dy));
					}
				}

				if (parent == previous)
				{
					index--;
				}
				this.model.add(parent, cells[i], index + i);

				if (this.isExtendParentsOnAdd() && this.isExtendParent(cells[i]))
				{
					this.extendParent(cells[i]);
				}
				this.constrainChild(cells[i]);

				if (source != null)
				{
					this.model.setTerminal(cells[i], source, true);
				}

				if (target != null)
				{
					this.model.setTerminal(cells[i], target, false);
				}
			}
			this.fireEvent(mxEvent.CELLS_ADDED, new mxEventObject(
			[
				cells,
				parent,
				index,
				source,
				target,
				absolute
			]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};
mxGraph.prototype.removeCells = function(cells, includeEdges)
{
	includeEdges = (includeEdges != null) ? includeEdges : true;

	if (cells == null)
	{
		cells = this.getDeletableCells(this.getSelectionCells());
	}

	if (includeEdges)
	{
		cells = this.getDeletableCells(this.addAllEdges(cells));
	}
	this.model.beginUpdate();

	try
	{
		this.cellsRemoved(cells);
		this.fireEvent(mxEvent.REMOVE_CELLS, new mxEventObject(
		[
			cells,
			includeEdges
		]));
	}
	finally
	{
		this.model.endUpdate();
	}
	return cells;
};
mxGraph.prototype.cellsRemoved = function(cells)
{
	if (cells != null && cells.length > 0)
	{
		var scale = this.view.scale;
		var tr = this.view.translate;
		this.model.beginUpdate();

		try
		{
			var hash = {};

			for (var i = 0; i < cells.length; i++)
			{
				var id = mxCellPath.create(cells[i]);
				hash[id] = cells[i];
			}

			for (var i = 0; i < cells.length; i++)
			{
				var edges = this.getConnections(cells[i]);

				for (var j = 0; j < edges.length; j++)
				{
					var id = mxCellPath.create(edges[j]);

					if (hash[id] == null)
					{
						var geo = this.model.getGeometry(edges[j]);

						if (geo != null)
						{
							var state = this.view.getState(edges[j]);

							if (state != null)
							{
								geo = geo.clone();
								var source = this.view.getVisibleTerminal(edges[j], true) == cells[i];
								var pts = state.absolutePoints;
								var n = (source) ? 0 : pts.length - 1;
								geo.setTerminalPoint(new mxPoint(pts[n].x / scale - tr.x, pts[n].y / scale - tr.y),
									source);
								this.model.setTerminal(edges[j], null, source);
								this.model.setGeometry(edges[j], geo);
							}
						}
					}
				}
				this.model.remove(cells[i]);
			}
			this.fireEvent(mxEvent.CELLS_REMOVED, new mxEventObject([cells]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};
mxGraph.prototype.splitEdge = function(edge, cells, newEdge, dx, dy)
{
	dx = dx || 0;
	dy = dy || 0;

	if (newEdge == null)
	{
		newEdge = this.cloneCells([edge])[0];
	}
	var parent = this.model.getParent(edge);
	var source = this.model.getTerminal(edge, true);
	this.model.beginUpdate();

	try
	{
		this.cellsMoved(cells, dx, dy, false, false);
		this.cellsAdded(cells, parent, this.model.getChildCount(parent), null, null, true);
		this.cellsAdded([newEdge], parent, this.model.getChildCount(parent), source, cells[0], false);
		this.cellConnected(edge, cells[0], true);
		this.fireEvent(mxEvent.SPLIT_EDGE, new mxEventObject(
		[
			edge,
			cells,
			newEdge,
			dx,
			dy
		]));
	}
	finally
	{
		this.model.endUpdate();
	}
	return newEdge;
};

mxGraph.prototype.toggleCells = function(show, cells, includeEdges)
{
	if (cells == null)
	{
		cells = this.getSelectionCells();
	}

	if (includeEdges)
	{
		cells = this.addAllEdges(cells);
	}
	this.model.beginUpdate();

	try
	{
		this.cellsToggled(cells, show);
		this.fireEvent(mxEvent.TOGGLE_CELLS, new mxEventObject(
		[
			show,
			cells,
			includeEdges
		]));
	}
	finally
	{
		this.model.endUpdate();
	}
	return cells;
};
mxGraph.prototype.cellsToggled = function(cells, show)
{
	if (cells != null && cells.length > 0)
	{
		this.model.beginUpdate();

		try
		{
			for (var i = 0; i < cells.length; i++)
			{
				this.model.setVisible(cells[i], show);
			}
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};

mxGraph.prototype.foldCells = function(collapse, recurse, cells)
{
	recurse = (recurse != null) ? recurse : false;

	if (cells == null)
	{
		cells = this.getFoldableCells(this.getSelectionCells(), collapse);
	}
	this.stopEditing(false);
	this.model.beginUpdate();

	try
	{
		this.cellsFolded(cells, collapse, recurse);
		this.fireEvent(mxEvent.FOLD_CELLS, new mxEventObject(
		[
			collapse,
			recurse,
			cells
		]));
	}
	finally
	{
		this.model.endUpdate();
	}
	return cells;
};
mxGraph.prototype.cellsFolded = function(cells, collapse, recurse)
{
	if (cells != null && cells.length > 0)
	{
		this.model.beginUpdate();

		try
		{
			for (var i = 0; i < cells.length; i++)
			{
				if (collapse != this.isCellCollapsed(cells[i]))
				{
					this.model.setCollapsed(cells[i], collapse);
					this.swapBounds(cells[i], collapse);

					if (this.isExtendParent(cells[i]))
					{
						this.extendParent(cells[i]);
					}

					if (recurse)
					{
						var children = this.model.getChildren(cells[i]);
						this.foldCells(children, collapse, recurse);
					}
				}
			}
			this.fireEvent(mxEvent.CELLS_FOLDED, new mxEventObject(
			[
				cells,
				collapse,
				recurse
			]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};
mxGraph.prototype.swapBounds = function(cell, willCollapse)
{
	if (cell != null)
	{
		var geo = this.model.getGeometry(cell);

		if (geo != null)
		{
			geo = geo.clone();
			this.updateAlternateBounds(cell, geo, willCollapse);
			geo.swap();
			this.model.setGeometry(cell, geo);
		}
	}
};
mxGraph.prototype.updateAlternateBounds = function(cell, geo, willCollapse)
{
	if (cell != null && geo != null)
	{
		if (geo.alternateBounds == null)
		{
			var bounds = geo;

			if (this.collapseToPreferredSize)
			{
				var tmp = this.getPreferredSizeForCell(cell);

				if (tmp != null)
				{
					bounds = tmp;
					var state = this.view.getState(cell);
					var style = (state != null) ? state.style : this.getCellStyle(cell);
					var startSize = mxUtils.getValue(style, mxConstants.STYLE_STARTSIZE);

					if (startSize > 0)
					{
						bounds.height = Math.max(bounds.height, startSize);
					}
				}
			}
			geo.alternateBounds = new mxRectangle(geo.x, geo.y, bounds.width, bounds.height);
		}
		else
		{
			geo.alternateBounds.x = geo.x;
			geo.alternateBounds.y = geo.y;
		}
	}
};
mxGraph.prototype.addAllEdges = function(cells)
{
	var allCells = cells.slice();
	allCells = allCells.concat(this.getAllEdges(cells));
	return allCells;
};
mxGraph.prototype.getAllEdges = function(cells)
{
	var edges = [];

	if (cells != null)
	{
		for (var i = 0; i < cells.length; i++)
		{
			var edgeCount = this.model.getEdgeCount(cells[i]);

			for (var j = 0; j < edgeCount; j++)
			{
				edges.push(this.model.getEdgeAt(cells[i], j));
			}
			var children = this.model.getChildren(cells[i]);
			edges = edges.concat(this.getAllEdges(children));
		}
	}
	return edges;
};

mxGraph.prototype.updateCellSize = function(cell, ignoreChildren)
{
	ignoreChildren = (ignoreChildren != null) ? ignoreChildren : false;
	this.model.beginUpdate();

	try
	{
		this.cellSizeUpdated(cell, ignoreChildren);
		this.fireEvent(mxEvent.UPDATE_CELL_SIZE, new mxEventObject(
		[
			cell,
			ignoreChildren
		]));
	}
	finally
	{
		this.model.endUpdate();
	}
	return cell;
};
mxGraph.prototype.cellSizeUpdated = function(cell, ignoreChildren)
{
	if (cell != null)
	{
		this.model.beginUpdate();

		try
		{
			var size = this.getPreferredSizeForCell(cell);
			var geo = this.model.getGeometry(cell);

			if (size != null && geo != null)
			{
				var collapsed = this.isCellCollapsed(cell);
				geo = geo.clone();

				if (this.isSwimlane(cell))
				{
					var state = this.view.getState(cell);
					var style = (state != null) ? state.style : this.getCellStyle(cell);
					var cellStyle = this.model.getStyle(cell);

					if (cellStyle == null)
					{
						cellStyle = '';
					}

					if (mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
					{
						cellStyle = mxUtils.setStyle(cellStyle, mxConstants.STYLE_STARTSIZE, size.height + 8);

						if (collapsed)
						{
							geo.height = size.height + 8;
						}
						geo.width = size.width;
					}
					else
					{
						cellStyle = mxUtils.setStyle(cellStyle, mxConstants.STYLE_STARTSIZE, size.width + 8);

						if (collapsed)
						{
							geo.width = size.width + 8;
						}
						geo.height = size.height;
					}
					this.model.setStyle(cell, cellStyle);
				}
				else
				{
					geo.width = size.width;
					geo.height = size.height;
				}

				if (!ignoreChildren && !collapsed)
				{
					var bounds = this.view.getBounds(this.model.getChildren(cell));

					if (bounds != null)
					{
						var tr = this.view.translate;
						var scale = this.view.scale;
						var width = (bounds.x + bounds.width) / scale - geo.x - tr.x;
						var height = (bounds.y + bounds.height) / scale - geo.y - tr.y;
						geo.width = Math.max(geo.width, width);
						geo.height = Math.max(geo.height, height);
					}
				}
				this.cellsResized([cell], [geo]);
			}
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};
mxGraph.prototype.getPreferredSizeForCell = function(cell)
{
	var result = null;

	if (cell != null)
	{
		var state = this.view.getState(cell);
		var style = (state != null) ? state.style : this.getCellStyle(cell);

		if (style != null && !this.model.isEdge(cell))
		{
			var fontSize = style[mxConstants.STYLE_FONTSIZE] || mxConstants.DEFAULT_FONTSIZE;
			var dx = 0;
			var dy = 0;

			if (this.getImage(state) != null || style[mxConstants.STYLE_IMAGE] != null)
			{
				if (style[mxConstants.STYLE_SHAPE] == mxConstants.SHAPE_LABEL)
				{
					if (style[mxConstants.STYLE_VERTICAL_ALIGN] == mxConstants.ALIGN_MIDDLE)
					{
						dx += style[mxConstants.STYLE_IMAGE_WIDTH] || mxLabel.prototype.imageSize;
					}

					if (style[mxConstants.STYLE_ALIGN] != mxConstants.ALIGN_CENTER)
					{
						dy += style[mxConstants.STYLE_IMAGE_HEIGHT] || mxLabel.prototype.imageSize;
					}
				}
			}
			dx += 2 * (style[mxConstants.STYLE_SPACING] || 0);
			dx += style[mxConstants.STYLE_SPACING_LEFT] || 0;
			dx += style[mxConstants.STYLE_SPACING_RIGHT] || 0;
			dy += 2 * (style[mxConstants.STYLE_SPACING] || 0);
			dy += style[mxConstants.STYLE_SPACING_TOP] || 0;
			dy += style[mxConstants.STYLE_SPACING_BOTTOM] || 0;

			var image = this.getFoldingImage(state);

			if (image != null)
			{
				dx += image.width + 8;
			}
			var value = this.getLabel(cell);

			if (value != null && value.length > 0)
			{
				if (!this.isHtmlLabel(cell))
				{
					value = value.replace(/\n/g, '<br>');
				}
				var size = mxUtils.getSizeForString(value, fontSize, style[mxConstants.STYLE_FONTFAMILY]);
				var width = size.width + dx;
				var height = size.height + dy;

				if (!mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
				{
					var tmp = height;
					height = width;
					width = tmp;
				}

				if (this.gridEnabled)
				{
					width = this.snap(width + this.gridSize / 2);
					height = this.snap(height + this.gridSize / 2);
				}
				result = new mxRectangle(0, 0, width, height);
			}
			else
			{
				var gs2 = 4 * this.gridSize;
				result = new mxRectangle(0, 0, gs2, gs2);
			}
		}
	}
	return result;
};
mxGraph.prototype.resizeCell = function(cell, bounds)
{
	return this.resizeCells([cell], [bounds])[0];
};
mxGraph.prototype.resizeCells = function(cells, boundsArray)
{
	this.model.beginUpdate();

	try
	{
		this.cellsResized(cells, boundsArray);
		this.fireEvent(mxEvent.RESIZE_CELLS, new mxEventObject(
		[
			cells,
			boundsArray
		]));
	}
	finally
	{
		this.model.endUpdate();
	}
	return cells;
};
mxGraph.prototype.cellsResized = function(cells, boundsArray)
{
	if (cells != null && boundsArray != null && cells.length == boundsArray.length)
	{
		this.model.beginUpdate();

		try
		{
			for (var i = 0; i < cells.length; i++)
			{
				var bounds = boundsArray[i];
				var geo = this.model.getGeometry(cells[i]);

				if (geo != null && (geo.x != bounds.x || geo.y != bounds.y || geo.width != bounds.width
					|| geo.height != bounds.height))
				{
					geo = geo.clone();

					if (geo.relative)
					{
						var offset = geo.offset;

						if (offset != null)
						{
							offset.x += bounds.x - geo.x;
							offset.y += bounds.y - geo.y;
						}
					}
					else
					{
						geo.x = bounds.x;
						geo.y = bounds.y;
					}
					geo.width = bounds.width;
					geo.height = bounds.height;
					this.model.setGeometry(cells[i], geo);

					if (this.isExtendParent(cells[i]))
					{
						this.extendParent(cells[i]);
					}
				}
			}

			if (this.resetEdgesOnResize)
			{
				this.resetEdges(cells);
			}
			this.fireEvent(mxEvent.CELLS_RESIZED, new mxEventObject(
			[
				cells,
				boundsArray
			]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};
mxGraph.prototype.extendParent = function(cell)
{
	if (cell != null)
	{
		var parent = this.model.getParent(cell);
		var p = this.model.getGeometry(parent);

		if (parent != null && p != null && !this.isCellCollapsed(parent))
		{
			var geo = this.model.getGeometry(cell);

			if (geo != null && (p.width < geo.x + geo.width || p.height < geo.y + geo.height))
			{
				p = p.clone();
				p.width = Math.max(p.width, geo.x + geo.width);
				p.height = Math.max(p.height, geo.y + geo.height);
				this.cellsResized([parent], [p]);
			}
		}
	}
};

mxGraph.prototype.importCells = function(cells, dx, dy, target, evt)
{
	return this.moveCells(cells, dx, dy, true, target, evt);
};
mxGraph.prototype.moveCells = function(cells, dx, dy, clone, target, evt)
{
	if (cells != null && (dx != 0 || dy != 0 || clone || target != null))
	{
		this.model.beginUpdate();

		try
		{
			if (clone)
			{
				cells = this.cloneCells(cells, this.isCloneInvalidEdges());

				if (target == null)
				{
					target = this.getDefaultParent();
				}
			}
			this.cellsMoved(cells, dx, dy, !clone && this.isDisconnectOnMove() && this.isAllowDanglingEdges(),
				target == null);

			if (target != null)
			{
				var index = this.model.getChildCount(target);
				this.cellsAdded(cells, target, index, null, null, true);
			}
			this.fireEvent(mxEvent.MOVE_CELLS, new mxEventObject(
			[
				cells,
				dx,
				dy,
				clone,
				target,
				evt
			]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
	return cells;
};
mxGraph.prototype.cellsMoved = function(cells, dx, dy, disconnect, constrain)
{
	if (cells != null && (dx != 0 || dy != 0))
	{
		this.model.beginUpdate();

		try
		{
			if (disconnect)
			{
				this.disconnectGraph(cells);
			}

			for (var i = 0; i < cells.length; i++)
			{
				this.translateCell(cells[i], dx, dy);

				if (constrain)
				{
					this.constrainChild(cells[i]);
				}
			}

			if (this.resetEdgesOnMove)
			{
				this.resetEdges(cells);
			}
			this.fireEvent(mxEvent.CELLS_MOVED, new mxEventObject(
			[
				cells,
				dx,
				dy,
				disconnect
			]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};
mxGraph.prototype.translateCell = function(cell, dx, dy)
{
	var geo = this.model.getGeometry(cell);

	if (geo != null)
	{
		geo = geo.translate(dx, dy);

		if (geo.relative && !this.model.isEdge(cell))
		{
			if (geo.offset == null)
			{
				geo.offset = new mxPoint(dx, dy);
			}
			else
			{
				geo.offset.X += dx;
				geo.offset.Y += dy;
			}
		}
		this.model.setGeometry(cell, geo);
	}
}
mxGraph.prototype.getCellContainmentArea = function(cell)
{
	if (cell != null && !this.model.isEdge(cell))
	{
		var parent = this.model.getParent(cell);

		if (parent == this.getDefaultParent() || parent == this.getCurrentRoot())
		{
			return this.getMaximumGraphBounds();
		}
		else
			(parent != null && parent != this.getDefaultParent())

		{
			var g = this.model.getGeometry(parent);

		if (g != null)
		{
			var x = 0;
			var y = 0;
			var w = g.width;
			var h = g.height;

			if (this.isSwimlane(parent))
			{
				var size = this.getStartSize(parent);
				x = size.width;
				w -= size.width;
				y = size.height;
				h -= size.height;
			}
			return new mxRectangle(x, y, w, h);
		}
		}
	}
	return null;
};
mxGraph.prototype.getMaximumGraphBounds = function()
{
	return this.maximumGraphBounds;
};
mxGraph.prototype.constrainChild = function(cell)
{
	if (cell != null)
	{
		var geo = this.model.getGeometry(cell);
		var area = (this.isConstrainChild(cell)) ? this.getCellContainmentArea(cell) : this.getMaximumGraphBounds();

		if (geo != null && area != null)
		{
			if (!geo.relative
				&& (geo.x < area.x || geo.y < area.y || area.width < geo.x + geo.width || area.height < geo.y
					+ geo.height))
			{
				var overlap = this.getOverlap(cell);

				if (area.width > 0)
				{
					geo.x = Math.min(geo.x, area.x + area.width - (1 - overlap) * geo.width);
				}

				if (area.height > 0)
				{
					geo.y = Math.min(geo.y, area.y + area.height - (1 - overlap) * geo.height);
				}
				geo.x = Math.max(geo.x, area.x - geo.width * overlap);
				geo.y = Math.max(geo.y, area.y - geo.height * overlap);
			}
		}
	}
};
mxGraph.prototype.resetEdges = function(cells)
{
	if (cells != null)
	{
		var hash = {};

		for (var i = 0; i < cells.length; i++)
		{
			var id = mxCellPath.create(cells[i]);
			hash[id] = cells[i];
		}
		this.model.beginUpdate();

		try
		{
			for (var i = 0; i < cells.length; i++)
			{
				var edges = this.model.getEdges(cells[i]);

				if (edges != null)
				{
					for (var j = 0; j < edges.length; j++)
					{
						var source = this.view.getVisibleTerminal(edges[j], true);
						var sourceId = mxCellPath.create(source);
						var target = this.view.getVisibleTerminal(edges[j], false);
						var targetId = mxCellPath.create(target);

						if (hash[sourceId] == null || hash[targetId] == null)
						{
							this.resetEdge(edges[j]);
						}
					}
				}
				this.resetEdges(this.model.getChildren(cells[i]));
			}
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};
mxGraph.prototype.resetEdge = function(edge)
{
	var geo = this.model.getGeometry(edge);

	if (geo != null && geo.points != null && geo.points.length > 0)
	{
		geo = geo.clone();
		geo.points = [];
		this.model.setGeometry(edge, geo);
	}
	return edge;
};

mxGraph.prototype.connectCell = function(edge, terminal, source)
{
	this.model.beginUpdate();

	try
	{
		this.cellConnected(edge, terminal, source);
		this.fireEvent(mxEvent.CONNECT_CELL, new mxEventObject(
		[
			edge,
			terminal,
			source
		]));
	}
	finally
	{
		this.model.endUpdate();
	}
	return edge;
};
mxGraph.prototype.cellConnected = function(edge, terminal, source)
{
	if (edge != null)
	{
		this.model.beginUpdate();

		try
		{
			this.model.setTerminal(edge, terminal, source);

			if (this.resetEdgesOnConnect)
			{
				this.resetEdge(edge);
			}
			this.fireEvent(mxEvent.CELL_CONNECTED, new mxEventObject(
			[
				edge,
				terminal,
				source
			]));
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};
mxGraph.prototype.disconnectGraph = function(cells)
{
	if (cells != null)
	{
		this.model.beginUpdate();

		try
		{
			var scale = this.view.scale;
			var tr = this.view.translate;
			var hash = {};

			for (var i = 0; i < cells.length; i++)
			{
				var id = mxCellPath.create(cells[i]);
				hash[id] = cells[i];
			}

			for (var i = 0; i < cells.length; i++)
			{
				if (this.model.isEdge(cells[i]))
				{
					var geo = this.model.getGeometry(cells[i]);

					if (geo != null)
					{
						var state = this.view.getState(cells[i]);
						var pstate = this.view.getState(this.model.getParent(cells[i]));

						if (state != null && pstate != null)
						{
							geo = geo.clone();
							var dx = -pstate.origin.x;
							var dy = -pstate.origin.y;
							var pts = state.absolutePoints;
							var src = this.model.getTerminal(cells[i], true);

							if (src != null && this.isCellDisconnectable(cells[i], src, true))
							{
								var srcId = mxCellPath.create(src);

								while (src != null && hash[srcId] == null)
								{
									src = this.model.getParent(src);
									srcId = mxCellPath.create(src);
								}

								if (src == null)
								{
									geo.setTerminalPoint(new mxPoint(pts[0].x / scale - tr.x + dx,
										pts[0].y / scale - tr.y + dy), true);
									this.model.setTerminal(cells[i], null, true);
								}
							}
							var trg = this.model.getTerminal(cells[i], false);

							if (trg != null && this.isCellDisconnectable(cells[i], trg, false))
							{
								var trgId = mxCellPath.create(trg);

								while (trg != null && hash[trgId] == null)
								{
									trg = this.model.getParent(trg);
									trgId = mxCellPath.create(trg);
								}

								if (trg == null)
								{
									var n = pts.length - 1;
									geo.setTerminalPoint(new mxPoint(pts[n].x / scale - tr.x + dx,
										pts[n].y / scale - tr.y + dy), false);
									this.model.setTerminal(cells[i], null, false);
								}
							}
							this.model.setGeometry(cells[i], geo);
						}
					}
				}
			}
		}
		finally
		{
			this.model.endUpdate();
		}
	}
};

mxGraph.prototype.getCurrentRoot = function()
{
	return this.view.currentRoot;
};
mxGraph.prototype.getTranslateForRoot = function(cell)
{
	return null;
};
mxGraph.prototype.getChildOffsetForCell = function(cell)
{
	return null;
};
mxGraph.prototype.enterGroup = function(cell)
{
	cell = cell || this.getSelectionCell();

	if (cell != null && this.isValidRoot(cell))
	{
		this.view.setCurrentRoot(cell);
		this.clearSelection();
	}
};
mxGraph.prototype.exitGroup = function()
{
	var root = this.model.getRoot();
	var current = this.getCurrentRoot();

	if (current != null)
	{
		var next = this.model.getParent(current);

		while (next != root && !this.isValidRoot(next) && this.model.getParent(next) != root)
		{
			next = this.model.getParent(next);
		}

		if (next == root || this.model.getParent(next) == root)
		{
			this.view.setCurrentRoot(null);
		}
		else
		{
			this.view.setCurrentRoot(next);
		}
		var state = this.view.getState(current);

		if (state != null)
		{
			this.setSelectionCell(current);
		}
	}
};
mxGraph.prototype.home = function()
{
	var current = this.getCurrentRoot();

	if (current != null)
	{
		this.view.setCurrentRoot(null);
		var state = this.view.getState(current);

		if (state != null)
		{
			this.setSelectionCell(current);
		}
	}
};
mxGraph.prototype.isValidRoot = function(cell)
{
	return (cell != null);
};

mxGraph.prototype.getGraphBounds = function()
{
	return this.view.getGraphBounds();
}
mxGraph.prototype.getCellBounds = function(cell, includeEdges, includeDescendants)
{
	var cells = [cell];

	if (includeEdges)
	{
		cells = cells.concat(this.model.getEdges(cell));
	}
	var result = this.view.getBounds(cells);

	if (includeDescendants)
	{
		var childCount = this.model.getChildCount(cell);

		for (var i = 0; i < childCount; i++)
		{
			var tmp = this.getCellBounds(this.model.getChildAt(cell, i), includeEdges, true);

			if (result != null)
			{
				result.add(tmp);
			}
			else
			{
				result = tmp;
			}
		}
	}
};
mxGraph.prototype.refresh = function(cell)
{
	this.view.clear(cell, cell == null);
	this.view.validate();
	this.sizeDidChange();
	this.fireEvent(mxEvent.REFRESH);
};
mxGraph.prototype.snap = function(value)
{
	if (this.gridEnabled)
	{
		value = Math.round(value / this.gridSize) * this.gridSize;
	}
	return value;
};
mxGraph.prototype.panGraph = function(dx, dy)
{
	var style = mxUtils.getCurrentStyle(this.container);

	if (mxUtils.hasScrollbars(this.container))
	{
		this.container.scrollLeft = -dx;
		this.container.scrollTop = -dy;
	}
	else
	{
		var canvas = this.view.getCanvas();

		if (this.dialect == mxConstants.DIALECT_SVG)
		{
			canvas.setAttribute('transform', 'translate(' + dx + ',' + dy + ')');

			if (dx == 0 && dy == 0)
			{
				if (this.shiftPreview != null)
				{
					this.shiftPreview.parentNode.removeChild(this.shiftPreview);
					this.shiftPreview = null;
					var child = this.container.firstChild;

					while (child != null)
					{
						if (child != canvas.parentNode)
						{
							if (child.style != null)
							{
								child.style.visibility = 'visible';
							}
						}
						child = child.nextSibling;
					}
				}
			}
			else
			{
				if (this.shiftPreview == null)
				{
					this.shiftPreview = document.createElement('div');
					var tmp = [];
					var child = this.container.firstChild;

					while (child != null)
					{
						if (child != canvas.parentNode)
						{
							tmp.push(mxUtils.getInnerHtml(child));

							if (child.style != null)
							{
								child.style.visibility = 'hidden';
							}
						}
						child = child.nextSibling;
					}
					this.shiftPreview.innerHTML = tmp.join('');
					this.shiftPreview.style.position = 'absolute';
					this.shiftPreview.style.overflow = 'visible';
					var pt = mxUtils.getOffset(this.container);
					this.shiftPreview.style.left = pt.x + 'px';
					this.shiftPreview.style.top = pt.y + 'px';
					this.container.appendChild(this.shiftPreview);
				}
				this.shiftPreview.style.left = dx + 'px';
				this.shiftPreview.style.top = dy + 'px';
			}
		}
		else if (this.dialect == mxConstants.DIALECT_VML)
		{
			canvas.setAttribute('coordorigin', (-dx) + ',' + (-dy));
		}
		else
		{
			if (dx == 0 && dy == 0)
			{
				if (this.shiftPreview != null)
				{
					this.shiftPreview.parentNode.removeChild(this.shiftPreview);
					canvas.style.visibility = 'visible';
					this.shiftPreview = null;
				}
			}
			else
			{
				if (this.shiftPreview == null)
				{
					this.shiftPreview = this.view.getDrawPane().cloneNode(false);
					var tmp = mxUtils.getInnerHtml(this.view.getBackgroundPane());
					tmp += mxUtils.getInnerHtml(this.view.getDrawPane());
					this.shiftPreview.innerHTML = tmp;
					var pt = mxUtils.getOffset(this.container);
					this.shiftPreview.style.position = 'absolute';
					this.shiftPreview.style.left = pt.x + 'px';
					this.shiftPreview.style.top = pt.y + 'px';
					canvas.style.visibility = 'hidden';
					this.container.appendChild(this.shiftPreview);
				}
				this.shiftPreview.style.left = dx + 'px';
				this.shiftPreview.style.top = dy + 'px';
			}
		}
	}
};
mxGraph.prototype.zoomIn = function()
{
	this.zoom(this.zoomFactor);
};
mxGraph.prototype.zoomOut = function()
{
	this.zoom(1 / this.zoomFactor);
};
mxGraph.prototype.zoomActual = function()
{
	this.view.translate.x = 0;
	this.view.translate.y = 0;
	this.view.setScale(1);
};
mxGraph.prototype.zoom = function(factor)
{
	var scale = this.view.scale * factor;
	var state = this.view.getState(this.getSelectionCell());

	if (this.keepSelectionVisibleOnZoom && state != null)
	{
		var rect = new mxRectangle(state.x * factor, state.y * factor, state.width * factor, state.height * factor);

		this.view.scale = scale;

		if (!this.scrollRectToVisible(rect))
		{
			this.view.revalidate();
		}
	}
	else if (this.centerZoom && !mxUtils.hasScrollbars(this.container))
	{
		var dx = this.container.offsetWidth;
		var dy = this.container.offsetHeight;

		if (factor > 1)
		{
			var f = (factor - 1) / (scale * 2);
			dx *= -f;
			dy *= -f;
		}
		else
		{
			var f = (1 / factor - 1) / (this.view.scale * 2);
			dx *= f;
			dy *= f;
		}
		this.view.scaleAndTranslate(scale, this.view.translate.x + dx, this.view.translate.y + dy);
	}
	else
	{
		this.view.setScale(scale);
	}
};
mxGraph.prototype.fit = function()
{
	var border = 10;
	var w1 = this.container.offsetWidth - 30 - 2 * border;
	var h1 = this.container.offsetHeight - 30 - 2 * border;
	var bounds = this.view.getGraphBounds();
	var w2 = bounds.width / this.view.scale;
	var h2 = bounds.height / this.view.scale;
	var s = Math.min(w1 / w2, h1 / h2);

	if (s > 0.1 && s < 8)
	{
		this.view.translate.x = (bounds.x != null)
			? this.view.translate.x - bounds.x / this.view.scale + border : border;
		this.view.translate.y = (bounds.y != null)
			? this.view.translate.y - bounds.y / this.view.scale + border : border;
		this.view.setScale(s);
	}
};
mxGraph.prototype.scrollCellToVisible = function(cell, center)
{
	var x = -this.view.translate.x;
	var y = -this.view.translate.y;
	var state = this.view.getState(cell);

	if (state != null)
	{
		var bounds = new mxRectangle(x + state.x, y + state.y, state.width, state.height);

		if (center && this.container != null)
		{
			var w = this.container.clientWidth;
			var h = this.container.clientHeight;
			bounds.x = bounds.getCenterX() - w / 2;
			bounds.width = w;
			bounds.y = bounds.getCenterY() - h / 2;
			bounds.height = h;
		}

		if (this.scrollRectToVisible(bounds))
		{
			this.view.setTranslate(this.view.translate.x, this.view.translate.y);
		}
	}
};
mxGraph.prototype.scrollRectToVisible = function(rect)
{
	if (rect != null)
	{
		var isChanged = false;

		if (mxUtils.hasScrollbars(this.container))
		{
			var c = this.container;
			var dx = c.scrollLeft - rect.x;

			if (dx > 0)
			{
				c.scrollLeft -= dx + 2;
			}
			else
			{
				dx = rect.x + rect.width - c.scrollLeft - c.clientWidth;

				if (dx > 0)
				{
					c.scrollLeft += dx + 2;
				}
			}
			var dy = c.scrollTop - rect.y;

			if (dy > 0)
			{
				c.scrollTop -= dy + 2;
			}
			else
			{
				dy = rect.y + rect.height - c.scrollTop - c.clientHeight;

				if (dy > 0)
				{
					c.scrollTop += dy + 2;
				}
			}
		}
		else
		{
			var x = -this.view.translate.x;
			var y = -this.view.translate.y;
			var w = this.container.offsetWidth;
			var h = this.container.offsetHeight;
			var s = this.view.scale;

			if (rect.x + rect.width > x + w)
			{
				this.view.translate.x -= (rect.x + rect.width - w - x) / s;
				isChanged = true;
			}

			if (rect.y + rect.height > y + h)
			{
				this.view.translate.y -= (rect.y + rect.height - h - y) / s;
				isChanged = true;
			}

			if (rect.x < x)
			{
				this.view.translate.x += (x - rect.x) / s;
				isChanged = true;
			}

			if (rect.y < y)
			{
				this.view.translate.y += (y - rect.y) / s;
				isChanged = true;
			}

			if (isChanged)
			{
				this.view.refresh();
			}
		}
	}
	return isChanged;
};
mxGraph.prototype.getCellGeometry = function(cell)
{
	return this.model.getGeometry(cell);
};
mxGraph.prototype.isCellVisible = function(cell)
{
	return this.model.isVisible(cell);
};
mxGraph.prototype.isCellCollapsed = function(cell)
{
	return this.model.isCollapsed(cell);
};
mxGraph.prototype.isCellConnectable = function(cell)
{
	return this.model.isConnectable(cell);
};
mxGraph.prototype.isOrthogonal = function(edge, vertex)
{
	var tmp = this.view.getEdgeStyle(edge);
	return tmp == mxEdgeStyle.ElbowConnector || tmp == mxEdgeStyle.SideToSide || tmp == mxEdgeStyle.TopToBottom
		|| tmp == mxEdgeStyle.EntityRelation;
};
mxGraph.prototype.isLoop = function(state)
{
	var src = this.view.getVisibleTerminal(state.cell, true);
	var trg = this.view.getVisibleTerminal(state.cell, false);
	return (src != null && src == trg);
};
mxGraph.prototype.isCloneEvent = function(evt)
{
	return mxEvent.isControlDown(evt);
};
mxGraph.prototype.isToggleEvent = function(evt)
{
	return mxEvent.isControlDown(evt);
};
mxGraph.prototype.isGridEnabledEvent = function(evt)
{
	return evt != null && !mxEvent.isAltDown(evt);
};
mxGraph.prototype.isConstrainedEvent = function(evt)
{
	return mxEvent.isShiftDown(evt);
};
mxGraph.prototype.isForceMarqueeEvent = function(evt)
{
	return mxEvent.isAltDown(evt) || mxEvent.isMetaDown(evt);
};

mxGraph.prototype.validationAlert = function(message)
{
	mxUtils.alert(message);
};
mxGraph.prototype.isEdgeValid = function(edge, source, target)
{
	return this.getEdgeValidationError(edge, source, target) == null;
};
mxGraph.prototype.getEdgeValidationError = function(edge, source, target)
{
	if (edge != null && this.model.getTerminal(edge, true) == null && this.model.getTerminal(edge, false) == null)
	{
		return null;
	}

	if (!this.allowLoops && source == target && source != null)
	{
		return '';
	}

	if (!this.isValidConnection(source, target))
	{
		return '';
	}

	if (source != null && target != null)
	{
		var error = '';

		if (!this.multigraph)
		{
			var tmp = this.model.getEdgesBetween(source, target, true);

			if (tmp.length > 1 || (tmp.length == 1 && tmp[0] != edge))
			{
				error += (mxResources.get(this.alreadyConnectedResource) || this.alreadyConnectedResource) + '\n';
			}
		}

		var sourceOut = this.model.getDirectedEdgeCount(source, true, edge);
		var targetIn = this.model.getDirectedEdgeCount(target, false, edge);

		for (var i = 0; i < this.multiplicities.length; i++)
		{
			var err = this.multiplicities[i].check(this, edge, source, target, sourceOut, targetIn);

			if (err != null)
			{
				error += err;
			}
		}
		var err = this.validateEdge(edge, source, target);

		if (err != null)
		{
			error += err;
		}
		return (error.length > 0) ? error : null;
	}
	return (this.allowDanglingEdges) ? null : '';
};
mxGraph.prototype.validateEdge = function(edge, source, target)
{
	return null;
};
mxGraph.prototype.validateGraph = function(cell, context)
{
	cell = (cell != null) ? cell : this.model.getRoot();
	context = (context != null) ? context : {};
	var isValid = true;
	var childCount = this.model.getChildCount(cell);

	for (var i = 0; i < childCount; i++)
	{
		var tmp = this.model.getChildAt(cell, i);
		var ctx = context;

		if (this.isValidRoot(tmp))
		{
			ctx = {};
		}
		var warn = this.validateGraph(tmp, ctx);

		if (warn != null)
		{
			var html = warn.replace(/\n/g, '<br>');
			var len = html.length;
			this.setCellWarning(tmp, html.substring(0, Math.max(0, len - 4)));
		}
		else
		{
			this.setCellWarning(tmp, null);
		}
		isValid = isValid && warn == null;
	}
	var warning = '';

	if (this.isCellCollapsed(cell) && !isValid)
	{
		warning += (mxResources.get(this.containsValidationErrorsResource) || this.containsValidationErrorsResource)
			+ '\n';
	}

	if (this.model.isEdge(cell))
	{
		warning += this.getEdgeValidationError(cell, this.model.getTerminal(cell, true),
			this.model.getTerminal(cell, false)) || '';
	}
	else
	{
		warning += this.getCellValidationError(cell) || '';
	}
	var err = this.validateCell(cell, context);

	if (err != null)
	{
		warning += err;
	}

	if (this.model.getParent(cell) == null)
	{
		this.view.validate();
	}
	return (warning.length > 0 || !isValid) ? warning : null;
};
mxGraph.prototype.getCellValidationError = function(cell)
{
	var outCount = this.model.getDirectedEdgeCount(cell, true);
	var inCount = this.model.getDirectedEdgeCount(cell, false);
	var value = this.model.getValue(cell);
	var error = '';

	for (var i = 0; i < this.multiplicities.length; i++)
	{
		var rule = this.multiplicities[i];

		if (rule.source && mxUtils.isNode(value, rule.type, rule.attr, rule.value)
			&& ((rule.max == 0 && outCount > 0) || (rule.min == 1 && outCount == 0) || (rule.max == 1 && outCount > 1)))
		{
			error += rule.countError + '\n';
		}
		else if (!rule.source && mxUtils.isNode(value, rule.type, rule.attr, rule.value)
			&& ((rule.max == 0 && inCount > 0) || (rule.min == 1 && inCount == 0) || (rule.max == 1 && inCount > 1)))
		{
			error += rule.countError + '\n';
		}
	}
	return (error.length > 0) ? error : null;
};
mxGraph.prototype.validateCell = function(cell, context)
{
	return null;
};

mxGraph.prototype.getBackgroundImage = function()
{
	return this.backgroundImage;
};
mxGraph.prototype.setBackgroundImage = function(image)
{
	this.backgroundImage = image;
};
mxGraph.prototype.getFoldingImage = function(state)
{
	if (state != null)
	{
		var tmp = this.isCellCollapsed(state.cell);

		if (this.isCellFoldable(state.cell, !tmp))
		{
			return (tmp) ? this.collapsedImage : this.expandedImage;
		}
	}
	return null;
};
mxGraph.prototype.convertValueToString = function(cell)
{
	var value = this.model.getValue(cell);

	if (value != null)
	{
		if (mxUtils.isNode(value))
		{
			return value.nodeName;
		}
		else if (typeof (value.toString) == 'function')
		{
			return value.toString();
		}
	}
	return '';
};
mxGraph.prototype.getLabel = function(cell)
{
	var result = '';
	var style = (cell != null) ? this.getCellStyle(cell) : null;

	if (cell != null && this.labelsVisible && !mxUtils.getValue(style, mxConstants.STYLE_NOLABEL, false))
	{
		result = this.convertValueToString(cell);
	}
	return result;
};
mxGraph.prototype.isHtmlLabel = function(cell)
{
	return this.htmlLabels;
};
mxGraph.prototype.isHtmlLabels = function()
{
	return this.htmlLabels;
};
mxGraph.prototype.setHtmlLabels = function(value)
{
	this.htmlLabels = value;
};
mxGraph.prototype.isWrapping = function(cell)
{
	return false;
};
mxGraph.prototype.isLabelClipped = function(cell)
{
	return false;
};
mxGraph.prototype.getTooltipForEvent = function(me)
{
	var tip = null;

	if (me.getTooltip() != null)
	{
		tip = me.getTooltip();
	}
	else if (me.getState() != null)
	{
		tip = this.getTooltipForCell(me.getCell());
	}
	return tip;
};
mxGraph.prototype.getTooltipForCell = function(cell)
{
	var tip = null;

	if (cell.getTooltip != null)
	{
		tip = cell.getTooltip();
	}
	else
	{
		tip = this.convertValueToString(cell);
	}
	return tip;
};
mxGraph.prototype.getCursorForCell = function(cell)
{
	return null;
};
mxGraph.prototype.getStartSize = function(swimlane)
{
	var result = new mxRectangle();
	var style = this.getCellStyle(swimlane);

	if (style != null)
	{
		var size = parseInt(style[mxConstants.STYLE_STARTSIZE]) || 0;

		if (mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true))
		{
			result.height = size;
		}
		else
		{
			result.width = size;
		}
	}
	return result;
};
mxGraph.prototype.getImage = function(state)
{
	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_IMAGE] : null;
};
mxGraph.prototype.getVerticalAlign = function(state)
{
	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_VERTICAL_ALIGN] : null;
};
mxGraph.prototype.getIndicatorColor = function(state)
{
	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_COLOR] : null;
};
mxGraph.prototype.getIndicatorGradientColor = function(state)
{
	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_GRADIENTCOLOR] : null;
};
mxGraph.prototype.getIndicatorShape = function(state)
{
	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_SHAPE] : null;
};
mxGraph.prototype.getIndicatorImage = function(state)
{
	return (state != null && state.style != null) ? state.style[mxConstants.STYLE_INDICATOR_IMAGE] : null;
};
mxGraph.prototype.getBorder = function()
{
	return this.border;
};
mxGraph.prototype.setBorder = function(border)
{
	this.border = border;
};
mxGraph.prototype.isSwimlane = function(cell)
{
	if (cell != null)
	{
		if (this.model.getParent(cell) != this.model.getRoot())
		{
			var state = this.view.getState(cell);
			var style = (state != null) ? state.style : this.getCellStyle(cell);

			if (style != null && !this.model.isEdge(cell))
			{
				return style[mxConstants.STYLE_SHAPE] == mxConstants.SHAPE_SWIMLANE;
			}
		}
	}
	return false;
};

mxGraph.prototype.isResizeContainer = function()
{
	return this.resizeContainer;
};
mxGraph.prototype.setResizeContainer = function(resizeContainer)
{
	this.resizeContainer = resizeContainer;
};
mxGraph.prototype.isEnabled = function()
{
	return this.enabled;
};
mxGraph.prototype.setEnabled = function(enabled)
{
	this.enabled = enabled;
};
mxGraph.prototype.isEscapeEnabled = function()
{
	return this.escapeEnabled;
};
mxGraph.prototype.setEscapeEnabled = function(value)
{
	this.escapeEnabled = value;
};
mxGraph.prototype.isInvokesStopCellEditing = function()
{
	return this.invokesStopCellEditing;
};
mxGraph.prototype.setInvokesStopCellEditing = function(value)
{
	this.invokesStopCellEditing = value;
};
mxGraph.prototype.setEnterStopsCellEditing = function()
{
	return this.enterStopsCellEditing;
};
mxGraph.prototype.isEnterStopsCellEditing = function(value)
{
	this.enterStopsCellEditing = value;
};
mxGraph.prototype.isCellLocked = function(cell)
{
	var geometry = this.model.getGeometry(cell);
	return this.isCellsLocked() || (geometry != null && this.model.isVertex(cell) && geometry.relative);
};
mxGraph.prototype.isCellsLocked = function()
{
	return this.cellsLocked;
};
mxGraph.prototype.setCellsLocked = function(value)
{
	this.cellsLocked = value;
};
mxGraph.prototype.getCloneableCells = function(cells)
{
	var self = this;
	return this.model.filterCells(cells, function(cell)
	{
		return self.isCellCloneable(cell);
	});
};
mxGraph.prototype.isCellCloneable = function(cell)
{
	return this.isCellsCloneable();
};
mxGraph.prototype.isCellsCloneable = function()
{
	return this.cellsCloneable;
}
mxGraph.prototype.setCellsCloneable = function(value)
{
	this.cellsCloneable = value;
};
mxGraph.prototype.getExportableCells = function(cells)
{
	var self = this;
	return this.model.filterCells(cells, function(cell)
	{
		return self.canExportCell(cell);
	});
};
mxGraph.prototype.canExportCell = function(cell)
{
	return this.exportEnabled;
};
mxGraph.prototype.getImportableCells = function(cells)
{
	var self = this;
	return this.model.filterCells(cells, function(cell)
	{
		return self.canImportCell(cell);
	});
};
mxGraph.prototype.canImportCell = function(cell)
{
	return this.importEnabled;
};
mxGraph.prototype.isCellSelectable = function(cell)
{
	return this.isCellsSelectable();
};
mxGraph.prototype.isCellsSelectable = function()
{
	return this.cellsSelectable;
};
mxGraph.prototype.setCellsSelectable = function(value)
{
	this.cellsSelectable = value;
};
mxGraph.prototype.getDeletableCells = function(cells)
{
	var self = this;
	return this.model.filterCells(cells, function(cell)
	{
		return self.isCellDeletable(cell);
	});
};
mxGraph.prototype.isCellDeletable = function(cell)
{
	return this.isCellsDeletable();
};
mxGraph.prototype.isCellsDeletable = function()
{
	return this.cellsDeletable;
};
mxGraph.prototype.setCellsDeletable = function(value)
{
	this.cellsDeletable = value;
};
mxGraph.prototype.isLabelMovable = function(cell)
{
	return !this.isCellLocked(cell) && ((this.model.isEdge(cell) && this.edgeLabelsMovable)
		|| (this.model.isVertex(cell) && this.vertexLabelsMovable));
};
mxGraph.prototype.getMovableCells = function(cells)
{
	var self = this;
	return this.model.filterCells(cells, function(cell)
	{
		return self.isCellMovable(cell);
	});
};
mxGraph.prototype.isCellMovable = function(cell)
{
	return this.isCellsMovable() && !this.isCellLocked(cell);
};
mxGraph.prototype.isCellsMovable = function()
{
	return this.cellsMovable;
};
mxGraph.prototype.setCellsMovable = function(value)
{
	this.cellsMovable = value;
};
mxGraph.prototype.isGridEnabled = function()
{
	return this.gridEnabled;
};
mxGraph.prototype.setGridEnabled = function(gridEnabled)
{
	this.gridEnabled = gridEnabled;
};
mxGraph.prototype.isSwimlaneNesting = function()
{
	return this.swimlaneNesting;
};
mxGraph.prototype.setSwimlaneNesting = function(swimlaneNesting)
{
	this.swimlaneNesting = swimlaneNesting;
};
mxGraph.prototype.isSwimlaneSelectionEnabled = function()
{
	return this.swimlaneSelectionEnabled;
};
mxGraph.prototype.setSwimlaneSelectionEnabled = function(swimlaneSelectionEnabled)
{
	this.swimlaneSelectionEnabled = swimlaneSelectionEnabled;
};
mxGraph.prototype.isMultigraph = function()
{
	return this.multigraph;
};
mxGraph.prototype.setMultigraph = function(multigraph)
{
	this.multigraph = multigraph;
};
mxGraph.prototype.isAllowLoops = function()
{
	return this.allowLoops;
};
mxGraph.prototype.setAllowDanglingEdges = function(allowDanglingEdges)
{
	this.allowDanglingEdges = allowDanglingEdges;
};
mxGraph.prototype.isAllowDanglingEdges = function()
{
	return this.allowDanglingEdges;
};
mxGraph.prototype.setConnectableEdges = function(connectableEdges)
{
	this.connectableEdges = connectableEdges;
};
mxGraph.prototype.isConnectableEdges = function()
{
	return this.connectableEdges;
};
mxGraph.prototype.setCloneInvalidEdges = function(cloneInvalidEdges)
{
	this.cloneInvalidEdges = cloneInvalidEdges;
};
mxGraph.prototype.isCloneInvalidEdges = function()
{
	return this.cloneInvalidEdges;
};
mxGraph.prototype.setAllowLoops = function(allowLoops)
{
	this.allowLoops = allowLoops;
};
mxGraph.prototype.isDisconnectOnMove = function()
{
	return this.disconnectOnMove;
};
mxGraph.prototype.setDisconnectOnMove = function(disconnectOnMove)
{
	this.disconnectOnMove = disconnectOnMove;
};
mxGraph.prototype.isDropEnabled = function()
{
	return this.dropEnabled;
};
mxGraph.prototype.setDropEnabled = function(value)
{
	this.dropEnabled = value;
};
mxGraph.prototype.isSplitEnabled = function()
{
	return this.splitEnabled;
};
mxGraph.prototype.setSplitEnabled = function(value)
{
	this.splitEnabled = value;
};
mxGraph.prototype.isCellResizable = function(cell)
{
	return this.isCellsResizable() && !this.isCellLocked(cell);
};
mxGraph.prototype.isCellsResizable = function()
{
	return this.cellsResizable;
};
mxGraph.prototype.setCellsResizable = function(value)
{
	this.cellsResizable = value;
};
mxGraph.prototype.isCellBendable = function(cell)
{
	return this.isCellsBendable() && !this.isCellLocked(cell);
};
mxGraph.prototype.isCellsBendable = function()
{
	return this.cellsBendable;
};
mxGraph.prototype.setCellsBendable = function(value)
{
	this.cellsBendable = value;
};
mxGraph.prototype.isCellEditable = function(cell)
{
	return this.isCellsEditable() && !this.isCellLocked(cell);
};
mxGraph.prototype.isCellsEditable = function()
{
	return this.cellsEditable;
};
mxGraph.prototype.setCellsEditable = function(value)
{
	this.cellsEditable = value;
};
mxGraph.prototype.isCellDisconnectable = function(cell, terminal, source)
{
	return this.isCellsDisconnectable() && !this.isCellLocked(cell);
};
mxGraph.prototype.isCellsDisconnectable = function()
{
	return this.cellsDisconnectable;
};
mxGraph.prototype.setCellsDisconnectable = function(value)
{
	this.cellsDisconnectable = value;
};
mxGraph.prototype.isValidSource = function(cell)
{
	return (cell == null && this.allowDanglingEdges)
		|| (cell != null && (!this.model.isEdge(cell) || this.connectableEdges) && this.isCellConnectable(cell));
};
mxGraph.prototype.isValidTarget = function(cell)
{
	return this.isValidSource(cell);
};
mxGraph.prototype.isValidConnection = function(source, target)
{
	return this.isValidSource(source) && this.isValidTarget(target);
};
mxGraph.prototype.setConnectable = function(connectable)
{
	this.connectionHandler.setEnabled(connectable);
};
mxGraph.prototype.isConnectable = function(connectable)
{
	return this.connectionHandler.isEnabled();
};
mxGraph.prototype.setTooltips = function(enabled)
{
	this.tooltipHandler.setEnabled(enabled);
};
mxGraph.prototype.setPanning = function(enabled)
{
	this.panningHandler.panningEnabled = enabled;
};
mxGraph.prototype.isEditing = function(cell)
{
	if (this.cellEditor != null)
	{
		var editingCell = this.cellEditor.getEditingCell();
		return (cell == null) ? editingCell != null : cell == editingCell;
	}
	return false;
};
mxGraph.prototype.isAutoSizeCell = function(cell)
{
	return this.isAutoSizeCells();
};
mxGraph.prototype.isAutoSizeCells = function()
{
	return this.autoSizeCells;
};
mxGraph.prototype.setAutoSizeCells = function(value)
{
	this.autoSizeCells = value;
};
mxGraph.prototype.isExtendParent = function(cell)
{
	return !this.getModel().isEdge(cell) && this.isExtendParents();
};
mxGraph.prototype.isExtendParents = function()
{
	return this.extendParents;
};
mxGraph.prototype.setExtendParents = function(value)
{
	this.extendParents = value;
};
mxGraph.prototype.isExtendParentsOnAdd = function()
{
	return this.extendParentsOnAdd;
};
mxGraph.prototype.setExtendParentsOnAdd = function(value)
{
	this.extendParentsOnAdd = value;
};
mxGraph.prototype.isConstrainChild = function(cell)
{
	return this.isConstrainChildren();
};
mxGraph.prototype.isConstrainChildren = function()
{
	return this.constrainChildren;
};
mxGraph.prototype.setConstrainChildren = function(value)
{
	this.constrainChildren = value;
};
mxGraph.prototype.getOverlap = function(cell)
{
	return (this.isAllowOverlapParent(cell)) ? this.defaultOverlap : 0;
};
mxGraph.prototype.isAllowOverlapParent = function(cell)
{
	return false;
};
mxGraph.prototype.getFoldableCells = function(cells, collapse)
{
	var self = this;
	return this.model.filterCells(cells, function(cell)
	{
		return self.isCellFoldable(cell, collapse);
	});
};
mxGraph.prototype.isCellFoldable = function(cell, collapse)
{
	return this.model.getChildCount(cell) > 0;
};
mxGraph.prototype.isValidDropTarget = function(cell, cells, evt)
{
	return cell != null && ((this.isSplitEnabled() && this.isSplitTarget(cell, cells, evt)) || (!this.model.isEdge(cell)
		&& (this.isSwimlane(cell) || (this.model.getChildCount(cell) > 0 && !this.isCellCollapsed(cell)))));
};
mxGraph.prototype.isSplitTarget = function(target, cells, evt)
{
	if (this.model.isEdge(target) && cells != null && cells.length == 1 && this.isCellConnectable(cells[0])
		&& this.getEdgeValidationError(target, this.model.getTerminal(target, true), cells[0]) == null)
	{
		var src = this.model.getTerminal(target, true);
		var trg = this.model.getTerminal(target, false);
		return (!this.model.isAncestor(cells[0], src) && !this.model.isAncestor(cells[0], trg));
	}
	return false;
};
mxGraph.prototype.getDropTarget = function(cells, evt, cell)
{
	if (!this.isSwimlaneNesting())
	{
		for (var i = 0; i < cells.length; i++)
		{
			if (this.isSwimlane(cells[i]))
			{
				return null;
			}
		}
	}
	var pt = mxUtils.convertPoint(this.container, evt.clientX, evt.clientY);
	var swimlane = this.getSwimlaneAt(pt.x, pt.y);

	if (cell == null)
	{
		cell = swimlane;
	}
	else if (swimlane != null)
	{

		var tmp = this.model.getParent(swimlane);

		while (tmp != null && this.isSwimlane(tmp) && tmp != cell)
		{
			tmp = this.model.getParent(tmp);
		}

		if (tmp == cell)
		{
			cell = swimlane;
		}
	}

	while (cell != null && !this.isValidDropTarget(cell, cells, evt) && !this.model.isLayer(cell))
	{
		cell = this.model.getParent(cell);
	}
	return (!this.model.isLayer(cell)) ? cell : null;
};

mxGraph.prototype.getDefaultParent = function()
{
	var parent = this.defaultParent;

	if (parent == null)
	{
		parent = this.getCurrentRoot();

		if (parent == null)
		{
			var root = this.model.getRoot();
			parent = this.model.getChildAt(root, 0);
		}
	}
	return parent;
};
mxGraph.prototype.setDefaultParent = function(cell)
{
	this.defaultParent = cell;
};
mxGraph.prototype.getSwimlane = function(cell)
{
	while (cell != null && !this.isSwimlane(cell))
	{
		cell = this.model.getParent(cell);
	}
	return cell;
};
mxGraph.prototype.getSwimlaneAt = function(x, y, parent)
{
	parent = parent || this.getDefaultParent();

	if (parent != null)
	{
		var childCount = this.model.getChildCount(parent);

		for (var i = 0; i < childCount; i++)
		{
			var child = this.model.getChildAt(parent, i);
			var result = this.getSwimlaneAt(x, y, child);

			if (result != null)
			{
				return result;
			}
			else if (this.isSwimlane(child))
			{
				var state = this.view.getState(child);

				if (this.isCellVisible(child) && this.intersects(state, x, y))
				{
					return child;
				}
			}
		}
	}
	return null;
};
mxGraph.prototype.getCellAt = function(x, y, parent, vertices, edges)
{
	vertices = vertices || true;
	edges = edges || true;
	parent = parent || this.getDefaultParent();

	if (parent != null)
	{
		var childCount = this.model.getChildCount(parent);

		for (var i = childCount - 1; i >= 0; i--)
		{
			var cell = this.model.getChildAt(parent, i);
			var result = this.getCellAt(x, y, cell, vertices, edges);

			if (result != null)
			{
				return result;
			}
			else if (this.isCellVisible(cell)
				&& (edges && this.model.isEdge(cell) || vertices && this.model.isVertex(cell)))
			{
				var state = this.view.getState(cell);

				if (this.intersects(state, x, y))
				{
					return cell;
				}
			}
		}
	}
	return null;
};
mxGraph.prototype.intersects = function(state, x, y)
{
	if (state != null)
	{
		var pts = state.absolutePoints;

		if (pts != null)
		{
			var t2 = this.tolerance * this.tolerance;
			var pt = pts[0];

			for (var i = 1; i < pts.length; i++)
			{
				var next = pts[i];
				var dist = mxUtils.ptSegDistSq(pt.x, pt.y, next.x, next.y, x, y);

				if (dist <= t2)
				{
					return true;
				}
				pt = next;
			}
		}
		else if (mxUtils.contains(state, x, y))
		{
			return true;
		}
	}
	return false;
};
mxGraph.prototype.hitsSwimlaneContent = function(swimlane, x, y)
{
	var state = this.getView().getState(swimlane);
	var size = this.getStartSize(swimlane);

	if (state != null)
	{
		var scale = this.getView().getScale();
		x -= state.x;
		y -= state.y;

		if (size.width > 0 && x > 0 && x > size.width * scale)
		{
			return true;
		}
		else if (size.height > 0 && y > 0 && y > size.height * scale)
		{
			return true;
		}
	}
	return false;
};
mxGraph.prototype.getChildVertices = function(parent)
{
	return this.getChildCells(parent, true, false);
}
mxGraph.prototype.getChildEdges = function(parent)
{
	return this.getChildCells(parent, false, true);
}
mxGraph.prototype.getChildCells = function(parent, vertices, edges)
{
	parent = (parent != null) ? parent : this.getDefaultParent();
	vertices = (vertices != null) ? vertices : false;
	edges = (edges != null) ? edges : false;
	var cells = this.model.getChildCells(parent, vertices, edges);
	var result = [];

	for (var i = 0; i < cells.length; i++)
	{
		if (this.isCellVisible(cells[i]))
		{
			result.push(cells[i]);
		}
	}
	return result;
}
mxGraph.prototype.getConnections = function(cell, parent)
{
	return this.getEdges(cell, parent, true, true, false);
}
mxGraph.prototype.getIncomingEdges = function(cell, parent)
{
	return this.getEdges(cell, parent, true, false, false);
}
mxGraph.prototype.getOutgoingEdges = function(cell, parent)
{
	return this.getEdges(cell, parent, false, true, false);
}
mxGraph.prototype.getEdges = function(cell, parent, incoming, outgoing, includeLoops)
{
	incoming = (incoming != null) ? incoming : true;
	outgoing = (outgoing != null) ? outgoing : true;
	includeLoops = (includeLoops != null) ? includeLoops : true;
	var edges = [];
	var isCollapsed = this.isCellCollapsed(cell);
	var childCount = this.model.getChildCount(cell);

	for (var i = 0; i < childCount; i++)
	{
		var child = this.model.getChildAt(cell, i);

		if (isCollapsed || !this.isCellVisible(child))
		{
			edges = edges.concat(this.model.getEdges(child, incoming, outgoing));
		}
	}
	edges = edges.concat(this.model.getEdges(cell, incoming, outgoing));
	var result = [];

	for (var i = 0; i < edges.length; i++)
	{
		var source = this.view.getVisibleTerminal(edges[i], true);
		var target = this.view.getVisibleTerminal(edges[i], false);

		if (includeLoops || ((source != target)
			&& (incoming && target == cell && (parent == null || this.model.getParent(source) == parent))
				|| (outgoing && source == cell && (parent == null || this.model.getParent(target) == parent))))
		{
			result.push(edges[i]);
		}
	}
	return result;
}
mxGraph.prototype.getOpposites = function(edges, terminal, sources, targets)
{
	sources = (sources != null) ? sources : true;
	targets = (targets != null) ? targets : true;
	var terminals = [];

	var hash = {};

	if (edges != null)
	{
		for (var i = 0; i < edges.length; i++)
		{
			var source = this.view.getVisibleTerminal(edges[i], true);
			var target = this.view.getVisibleTerminal(edges[i], false);

			if (source == terminal && target != null && target != terminal && targets)
			{
				var id = mxCellPath.create(target);

				if (hash[id] == null)
				{
					hash[id] = target;
					terminals.push(target);
				}
			}
			else if (target == terminal && source != null && source != terminal && sources)
			{
				var id = mxCellPath.create(source);

				if (hash[id] == null)
				{
					hash[id] = source;
					terminals.push(source);
				}
			}
		}
	}
	return terminals;
}
mxGraph.prototype.getEdgesBetween = function(source, target, directed)
{
	var edges = this.getEdges(source);
	var result = [];

	for (var i = 0; i < edges.length; i++)
	{
		var src = this.view.getVisibleTerminal(edges[i], true);
		var trg = this.view.getVisibleTerminal(edges[i], false);

		if (trg == target || (!directed && src == target))
		{
			result.push(edges[i]);
		}
	}
	return result;
}
mxGraph.prototype.getPointForEvent = function(evt)
{
	var p = mxUtils.convertPoint(this.container, evt.clientX, evt.clientY);
	var s = this.view.scale;
	var tr = this.view.translate;
	p.x = this.snap(p.x / s - tr.x - this.gridSize / 2);
	p.y = this.snap(p.y / s - tr.y - this.gridSize / 2);
	return p;
};
mxGraph.prototype.getCells = function(x, y, width, height, parent, result)
{
	var result = result || [];

	if (width > 0 || height > 0)
	{
		var right = x + width;
		var bottom = y + height;
		parent = parent || this.getDefaultParent();

		if (parent != null)
		{
			var childCount = this.model.getChildCount(parent);

			for (var i = 0; i < childCount; i++)
			{
				var cell = this.model.getChildAt(parent, i);
				var state = this.view.getState(cell);

				if (this.isCellVisible(cell) && state != null)
				{
					if (state.x >= x && state.y >= y && state.x + state.width <= right && state.y
						+ state.height <= bottom)
					{
						result.push(cell);
					}
					else
					{
						this.getCells(x, y, width, height, cell, result);
					}
				}
			}
		}
	}
	return result;
};
mxGraph.prototype.getCellsBeyond = function(x0, y0, parent, rightHalfpane, bottomHalfpane)
{
	var result = [];

	if (rightHalfpane || bottomHalfpane)
	{
		if (parent == null)
		{
			parent = this.getDefaultParent();
		}

		if (parent != null)
		{
			var childCount = this.model.getChildCount(parent);

			for (var i = 0; i < childCount; i++)
			{
				var child = this.model.getChildAt(parent, i);
				var state = this.view.getState(child);

				if (this.isCellVisible(child) && state != null)
				{
					if ((!rightHalfpane || state.x >= x0) && (!bottomHalfpane || state.y >= y0))
					{
						result.push(child);
					}
				}
			}
		}
	}
	return result;
};
mxGraph.prototype.findTreeRoots = function(parent, isolate, invert)
{
	isolate = (isolate != null) ? isolate : false;
	invert = (invert != null) ? invert : false;
	var roots = [];

	if (parent != null)
	{
		var model = this.getModel();
		var childCount = model.getChildCount(parent);
		var best = null;
		var maxDiff = 0;

		for (var i = 0; i < childCount; i++)
		{
			var cell = model.getChildAt(parent, i);

			if (this.model.isVertex(cell) && this.isCellVisible(cell))
			{
				var conns = this.getConnections(cell, (isolate) ? parent : null);
				var fanOut = 0;
				var fanIn = 0;

				for (var j = 0; j < conns.length; j++)
				{
					var src = this.view.getVisibleTerminal(conns[j], true);

					if (src == cell)
					{
						fanOut++;
					}
					else
					{
						fanIn++;
					}
				}

				if ((invert && fanOut == 0 && fanIn > 0) || (!invert && fanIn == 0 && fanOut > 0))
				{
					roots.push(cell);
				}
				var diff = (invert) ? fanIn - fanOut : fanOut - fanIn;

				if (diff > maxDiff)
				{
					maxDiff = diff;
					best = cell;
				}
			}
		}

		if (roots.length == 0 && best != null)
		{
			roots.push(best);
		}
	}
	return roots;
};
mxGraph.prototype.traverse = function(vertex, directed, func, edge, visited)
{
	if (func != null && vertex != null)
	{
		directed = (directed != null) ? directed : true;
		visited = visited || [];
		var id = mxCellPath.create(vertex);

		if (visited[id] == null)
		{
			visited[id] = vertex;
			var result = func(vertex, edge);

			if (result == null || result)
			{
				var edgeCount = this.model.getEdgeCount(vertex);

				if (edgeCount > 0)
				{
					for (var i = 0; i < edgeCount; i++)
					{
						var e = this.model.getEdgeAt(vertex, i);
						var isSource = this.model.getTerminal(e, true) == vertex;

						if (!directed || isSource)
						{
							var next = this.model.getTerminal(e, !isSource);
							this.traverse(next, directed, func, e, visited);
						}
					}
				}
			}
		}
	}
};

mxGraph.prototype.isCellSelected = function(cell)
{
	return this.getSelectionModel().isSelected(cell);
};
mxGraph.prototype.isSelectionEmpty = function()
{
	return this.getSelectionModel().isEmpty();
};
mxGraph.prototype.clearSelection = function()
{
	return this.getSelectionModel().clear();
};
mxGraph.prototype.getSelectionCount = function()
{
	return this.getSelectionModel().cells.length;
};
mxGraph.prototype.getSelectionCell = function()
{
	return this.getSelectionModel().cells[0];
};
mxGraph.prototype.getSelectionCells = function()
{
	return this.getSelectionModel().cells.slice();
};
mxGraph.prototype.setSelectionCell = function(cell)
{
	this.getSelectionModel().setCell(cell);
};
mxGraph.prototype.setSelectionCells = function(cells)
{
	this.getSelectionModel().setCells(cells);
};
mxGraph.prototype.addSelectionCell = function(cell)
{
	this.getSelectionModel().addCell(cell);
};
mxGraph.prototype.addSelectionCells = function(cells)
{
	this.getSelectionModel().addCells(cells);
};
mxGraph.prototype.removeSelectionCell = function(cell)
{
	this.getSelectionModel().removeCell(cell);
};
mxGraph.prototype.removeSelectionCells = function(cells)
{
	this.getSelectionModel().removeCells(cells);
};
mxGraph.prototype.selectRegion = function(rect, evt)
{
	var cells = this.getCells(rect.x, rect.y, rect.width, rect.height);
	this.selectCellsForEvent(cells, evt);
	return cells;
};
mxGraph.prototype.selectNextCell = function()
{
	this.selectCell(true);
}
mxGraph.prototype.selectPreviousCell = function()
{
	this.selectCell();
}
mxGraph.prototype.selectParentCell = function()
{
	this.selectCell(false, true);
}
mxGraph.prototype.selectChildCell = function()
{
	this.selectCell(false, false, true);
}
mxGraph.prototype.selectCell = function(isNext, isParent, isChild)
{
	var sel = this.selectionModel;
	var cell = (sel.cells.length > 0) ? sel.cells[0] : null;

	if (sel.cells.length > 1)
	{
		sel.clear();
	}
	var parent = (cell != null) ? this.model.getParent(cell) : this.getDefaultParent();
	var childCount = this.model.getChildCount(parent);

	if (cell == null && childCount > 0)
	{
		var child = this.model.getChildAt(parent, 0);
		this.setSelectionCell(child);
	}
	else if ((cell == null || isParent) && this.view.getState(parent) != null && this.model.getGeometry(parent) != null)
	{
		if (this.getCurrentRoot() != parent)
		{
			this.setSelectionCell(parent);
		}
	}
	else if (cell != null && isChild)
	{
		var tmp = this.model.getChildCount(cell);

		if (tmp > 0)
		{
			var child = this.model.getChildAt(cell, 0);
			this.setSelectionCell(child);
		}
	}
	else if (childCount > 0)
	{
		var i = parent.getIndex(cell);

		if (isNext)
		{
			i++;
			var child = this.model.getChildAt(parent, i % childCount);
			this.setSelectionCell(child);
		}
		else
		{
			i--;
			var index = (i < 0) ? childCount - 1 : i;
			var child = this.model.getChildAt(parent, index);
			this.setSelectionCell(child);
		}
	}
};
mxGraph.prototype.selectAll = function(parent)
{
	parent = parent || this.getDefaultParent();
	var children = this.model.getChildren(parent);

	if (children != null)
	{
		this.setSelectionCells(children);
	}
};
mxGraph.prototype.selectVertices = function(parent)
{
	this.selectCells(true, false, parent);
};
mxGraph.prototype.selectEdges = function(parent)
{
	this.selectCells(false, true, parent);
};
mxGraph.prototype.selectCells = function(vertices, edges, parent)
{
	parent = parent || this.getDefaultParent();
	var self = this;
	var filter = function(cell)
	{
		return self.view.getState(cell) != null && self.model.getChildCount(cell) == 0
			&& ((self.model.isVertex(cell) && vertices) || (self.model.isEdge(cell) && edges));
	}
	var cells = this.model.filterDescendants(filter, parent);
	this.setSelectionCells(cells);
};
mxGraph.prototype.selectCellForEvent = function(cell, evt)
{
	var isSelected = this.isCellSelected(cell);

	if (this.isToggleEvent(evt))
	{
		if (isSelected)
		{
			this.removeSelectionCell(cell);
		}
		else
		{
			this.addSelectionCell(cell);
		}
	}
	else if (!isSelected || this.getSelectionCount() != 1)
	{
		this.setSelectionCell(cell);
	}
};
mxGraph.prototype.selectCellsForEvent = function(cells, evt)
{
	if (this.isToggleEvent(evt))
	{
		this.addSelectionCells(cells);
	}
	else
	{
		this.setSelectionCells(cells);
	}
};

mxGraph.prototype.createHandler = function(state)
{
	if (state != null)
	{
		if (this.model.isEdge(state.cell))
		{
			var style = this.view.getEdgeStyle(state);

			if (this.isLoop(state) || style == mxEdgeStyle.ElbowConnector || style == mxEdgeStyle.SideToSide
				|| style == mxEdgeStyle.TopToBottom)
			{
				state.handler = new mxElbowEdgeHandler(state);
			}
			else
			{
				state.handler = new mxEdgeHandler(state);
			}
		}
		else
		{
			state.handler = new mxVertexHandler(state);
		}
	}
};
mxGraph.prototype.redrawHandler = function(state)
{
	if (state != null && state.handler != null)
	{
		state.handler.redraw();
	}
};
mxGraph.prototype.hasHandler = function(state)
{
	return state != null && state.handler != null;
};
mxGraph.prototype.destroyHandler = function(state)
{
	if (state != null && state.handler != null)
	{
		state.handler.destroy();
		state.handler = null;
	}
};

mxGraph.prototype.addMouseListener = function(listener)
{
	if (this.mouseListeners == null)
	{
		this.mouseListeners = [];
	}
	this.mouseListeners.push(listener);
};
mxGraph.prototype.removeMouseListener = function(listener)
{
	if (this.mouseListeners != null)
	{
		for (var i = 0; i < this.mouseListeners.length; i++)
		{
			if (this.mouseListeners[i] == listener)
			{
				this.mouseListeners.splice(i, 1);
				break;
			}
		}
	}
};
mxGraph.prototype.fireMouseEvent = function(evtName, me, source)
{
	if (source == null)
	{
		source = this;
	}

	if (!mxClient.IS_IE && evtName == mxEvent.MOUSE_DOWN && this.activeElement != null)
	{
		this.activeElement.blur();
	}

	if (evtName == mxEvent.MOUSE_DOWN)
	{
		this.isMouseDown = true;
	}

	if ((evtName != mxEvent.MOUSE_UP || this.isMouseDown) && me.getEvent().detail != 2)
	{
		if (evtName == mxEvent.MOUSE_UP)
		{
			this.isMouseDown = false;
		}

		if (!this.isEditing()
			&& (mxClient.IS_OP || mxClient.IS_SF || mxClient.IS_GC || me.getEvent().target != this.container))
		{
			if (evtName == mxEvent.MOUSE_MOVE && this.isMouseDown && this.autoScroll)
			{
				var pt = mxUtils.convertPoint(this.container, me.getX(), me.getY());
				this.scrollPointToVisible(pt.x, pt.y, this.autoExtend);
			}

			if (this.mouseListeners != null)
			{
				var args =
				[
					source,
					me
				];

				me.getEvent().returnValue = true;

				for (var i = 0; i < this.mouseListeners.length; i++)
				{
					var l = this.mouseListeners[i];

					if (evtName == mxEvent.MOUSE_DOWN)
					{
						l.mouseDown.apply(l, args);
					}
					else if (evtName == mxEvent.MOUSE_MOVE)
					{
						l.mouseMove.apply(l, args);
					}
					else if (evtName == mxEvent.MOUSE_UP)
					{
						l.mouseUp.apply(l, args);
					}
				}
			}

			if (evtName == mxEvent.MOUSE_UP)
			{
				this.click(me.getEvent(), me.getCell());
			}
		}
	}
};
mxGraph.prototype.destroy = function()
{
	if (!this.destroyed)
	{
		this.destroyed = true;

		if (this.tooltipHandler != null)
		{
			this.tooltipHandler.destroy();
		}

		if (this.panningHandler != null)
		{
			this.panningHandler.destroy();
		}

		if (this.connectionHandler != null)
		{
			this.connectionHandler.destroy();
		}

		if (this.graphHandler != null)
		{
			this.graphHandler.destroy();
		}

		if (this.cellEditor != null)
		{
			this.cellEditor.destroy();
		}

		if (this.view != null)
		{
			this.view.destroy();
		}

		if (this.focusHandler != null)
		{
			mxEvent.removeListener(document.body, 'focus', this.focusHandler);
			this.focusHandler = null;
		}

		if (this.blurHandler != null)
		{
			mxEvent.removeListener(document.body, 'blur', this.blurHandler);
			this.blurHandler = null;
		}
		this.activeElement = null;
		this.container = null;
	}
};